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>,
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) {

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 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> {