moving time traveler over to new stackable state model. and with that,

the stackable state model is good enough!
This commit is contained in:
Dustin Carlino 2019-06-23 18:45:46 -07:00
parent 6905595a05
commit 8c67267d28
2 changed files with 90 additions and 133 deletions

View File

@ -20,8 +20,7 @@ pub struct SandboxMode {
following: Option<TripID>, following: Option<TripID>,
route_viewer: route_viewer::RouteViewer, route_viewer: route_viewer::RouteViewer,
show_activity: show_activity::ShowActivity, show_activity: show_activity::ShowActivity,
time_travel: time_travel::TimeTravel, pub time_travel: time_travel::InactiveTimeTravel,
// TODO Not while Spawning or TimeTraveling or ExploringRoute...
common: CommonState, common: CommonState,
menu: ModalMenu, menu: ModalMenu,
} }
@ -33,7 +32,7 @@ impl SandboxMode {
following: None, following: None,
route_viewer: route_viewer::RouteViewer::Inactive, route_viewer: route_viewer::RouteViewer::Inactive,
show_activity: show_activity::ShowActivity::Inactive, show_activity: show_activity::ShowActivity::Inactive,
time_travel: time_travel::TimeTravel::new(), time_travel: time_travel::InactiveTimeTravel::new(),
common: CommonState::new(), common: CommonState::new(),
menu: ModalMenu::new( menu: ModalMenu::new(
"Sandbox Mode", "Sandbox Mode",
@ -148,9 +147,7 @@ impl State for SandboxMode {
self.route_viewer.event(ctx, ui, &mut self.menu); self.route_viewer.event(ctx, ui, &mut self.menu);
self.show_activity.event(ctx, ui, &mut self.menu); self.show_activity.event(ctx, ui, &mut self.menu);
if self.menu.action("start time traveling") { if self.menu.action("start time traveling") {
//self.state = State::TimeTraveling; return self.time_travel.start(ctx, ui);
//self.time_travel.start(ctx, ui);
//return EventLoopMode::InputOnly;
} }
if self.menu.action("scoreboard") { if self.menu.action("scoreboard") {
return Transition::Push(Box::new(score::Scoreboard::new(ctx, ui))); 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.show_activity.draw(g, ui);
self.menu.draw(g); self.menu.draw(g);
self.speed.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) { fn on_suspend(&mut self, _: &mut UI) {

View File

@ -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 abstutil::MultiMap;
use ezgui::{hotkey, EventCtx, GfxCtx, ItemSlider, Key, Text}; use ezgui::{hotkey, EventCtx, GfxCtx, ItemSlider, Key, Text};
use geom::Duration; use geom::Duration;
@ -6,15 +9,12 @@ use map_model::{Map, Traversable};
use sim::{CarID, DrawCarInput, DrawPedestrianInput, GetDrawAgents, PedestrianID}; use sim::{CarID, DrawCarInput, DrawPedestrianInput, GetDrawAgents, PedestrianID};
use std::collections::BTreeMap; use std::collections::BTreeMap;
pub enum TimeTravel { pub struct InactiveTimeTravel {
Active(ItemSlider<StateAtTime>), should_record: bool,
Inactive { moments: Vec<(StateAtTime, Text)>,
should_record: bool,
moments: Vec<(StateAtTime, Text)>,
},
} }
pub struct StateAtTime { struct StateAtTime {
time: Duration, time: Duration,
cars: BTreeMap<CarID, DrawCarInput>, cars: BTreeMap<CarID, DrawCarInput>,
peds: BTreeMap<PedestrianID, DrawPedestrianInput>, peds: BTreeMap<PedestrianID, DrawPedestrianInput>,
@ -22,139 +22,114 @@ pub struct StateAtTime {
peds_per_traversable: MultiMap<Traversable, PedestrianID>, peds_per_traversable: MultiMap<Traversable, PedestrianID>,
} }
impl TimeTravel { impl InactiveTimeTravel {
pub fn new() -> TimeTravel { pub fn new() -> InactiveTimeTravel {
TimeTravel::Inactive { InactiveTimeTravel {
should_record: false, should_record: false,
moments: Vec::new(), 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... // In case we weren't already...
match self {
TimeTravel::Inactive {
ref mut should_record,
..
} => {
*should_record = true;
}
TimeTravel::Active(_) => unreachable!(),
}
self.record(ui); self.record(ui);
// TODO More cleanly? // Temporarily move our state into the new one.
let items = match self { let items = std::mem::replace(&mut self.moments, Vec::new());
TimeTravel::Inactive {
ref mut moments, .. return Transition::Push(Box::new(TimeTraveler {
} => std::mem::replace(moments, Vec::new()), slider: ItemSlider::new(
TimeTravel::Active(_) => unreachable!(), items,
}; "Time Traveler",
*self = TimeTravel::Active(ItemSlider::new( "moment",
items, vec![(hotkey(Key::Escape), "quit")],
"Time Traveler", ctx,
"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 // TODO Now that we take big jumps forward in the source sim, the time traveler sees the same
// granularity when replaying. // granularity when replaying.
pub fn record(&mut self, ui: &UI) { pub fn record(&mut self, ui: &UI) {
match self { if !self.should_record {
TimeTravel::Inactive { return;
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!(),
} }
}
// Returns true if done. let map = &ui.primary.map;
pub fn event(&mut self, ctx: &mut EventCtx) -> bool { let sim = &ui.primary.sim;
match self { let now = sim.time();
TimeTravel::Inactive { .. } => unreachable!(),
TimeTravel::Active(ref mut slider) => {
slider.event(ctx);
ctx.canvas.handle_event(ctx.input);
if slider.action("quit") { if let Some((ref state, _)) = self.moments.last() {
*self = TimeTravel::Inactive { // Already have this
should_record: true, if now == state.time {
moments: slider.consume_all_items(), return;
}; }
return true; // We just loaded a new savestate or reset or something. Clear out our memory.
} if now < state.time {
false self.moments.clear();
} }
} }
}
pub fn draw(&self, g: &mut GfxCtx) { let mut state = StateAtTime {
match self { time: now,
TimeTravel::Inactive { .. } => unreachable!(), cars: BTreeMap::new(),
TimeTravel::Active(ref slider) => { peds: BTreeMap::new(),
slider.draw(g); 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);
fn get_current_state(&self) -> &StateAtTime { state.peds.insert(draw.id, draw);
match self {
TimeTravel::Inactive { .. } => unreachable!(),
TimeTravel::Active(ref slider) => slider.get().1,
} }
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 { fn time(&self) -> Duration {
self.get_current_state().time self.get_current_state().time
} }
fn step_count(&self) -> usize { fn step_count(&self) -> usize {
match self { self.slider.get().0
TimeTravel::Inactive { .. } => unreachable!(),
TimeTravel::Active(ref slider) => slider.get().0,
}
} }
fn get_draw_car(&self, id: CarID, _map: &Map) -> Option<DrawCarInput> { fn get_draw_car(&self, id: CarID, _map: &Map) -> Option<DrawCarInput> {