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>,
|
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) {
|
||||||
|
@ -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> {
|
||||||
|
Loading…
Reference in New Issue
Block a user