mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 23:43:25 +03:00
share time stepping controls btwn sandbox and abtest mode
This commit is contained in:
parent
e137fcf15c
commit
b9c9a77f40
@ -1,12 +1,12 @@
|
||||
mod score;
|
||||
pub mod setup;
|
||||
|
||||
use crate::common::{CommonState, SpeedControls};
|
||||
use crate::common::{time_controls, CommonState, SpeedControls};
|
||||
use crate::game::{State, Transition};
|
||||
use crate::render::MIN_ZOOM_FOR_DETAIL;
|
||||
use crate::ui::{PerMapUI, UI};
|
||||
use ezgui::{hotkey, Color, EventCtx, EventLoopMode, GeomBatch, GfxCtx, Key, ModalMenu, Text};
|
||||
use geom::{Circle, Distance, Duration, Line, PolyLine};
|
||||
use geom::{Circle, Distance, Line, PolyLine};
|
||||
use map_model::{Map, LANE_THICKNESS};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use sim::{Sim, TripID};
|
||||
@ -35,9 +35,11 @@ impl ABTestMode {
|
||||
(hotkey(Key::RightBracket), "speed up"),
|
||||
(hotkey(Key::Space), "pause/resume"),
|
||||
(hotkey(Key::M), "step forwards 0.1s"),
|
||||
(hotkey(Key::N), "step forwards 10 mins"),
|
||||
(hotkey(Key::B), "jump to specific time"),
|
||||
(hotkey(Key::S), "swap"),
|
||||
(hotkey(Key::D), "diff all trips"),
|
||||
(hotkey(Key::B), "stop diffing trips"),
|
||||
(hotkey(Key::A), "stop diffing trips"),
|
||||
(hotkey(Key::Q), "scoreboard"),
|
||||
(hotkey(Key::O), "save state"),
|
||||
],
|
||||
@ -133,12 +135,18 @@ impl State for ABTestMode {
|
||||
}
|
||||
|
||||
if let Some(dt) = self.speed.event(ctx, &mut self.menu, ui.primary.sim.time()) {
|
||||
self.step(dt, ui, ctx);
|
||||
ui.primary.sim.step(&ui.primary.map, dt);
|
||||
{
|
||||
let s = ui.secondary.as_mut().unwrap();
|
||||
s.sim.step(&s.map, dt);
|
||||
}
|
||||
self.recalculate_stuff(ui, ctx);
|
||||
}
|
||||
|
||||
if self.speed.is_paused() {
|
||||
if self.menu.action("step forwards 0.1s") {
|
||||
self.step(Duration::seconds(0.1), ui, ctx);
|
||||
if let Some(t) = time_controls(ctx, ui, &mut self.menu) {
|
||||
// TODO Need to trigger recalculate_stuff in a few cases...
|
||||
return t;
|
||||
}
|
||||
Transition::Keep
|
||||
} else {
|
||||
@ -173,15 +181,6 @@ impl State for ABTestMode {
|
||||
}
|
||||
|
||||
impl ABTestMode {
|
||||
fn step(&mut self, dt: Duration, ui: &mut UI, ctx: &EventCtx) {
|
||||
ui.primary.sim.step(&ui.primary.map, dt);
|
||||
{
|
||||
let s = ui.secondary.as_mut().unwrap();
|
||||
s.sim.step(&s.map, dt);
|
||||
}
|
||||
self.recalculate_stuff(ui, ctx);
|
||||
}
|
||||
|
||||
fn recalculate_stuff(&mut self, ui: &mut UI, ctx: &EventCtx) {
|
||||
if let Some(diff) = self.diff_trip.take() {
|
||||
self.diff_trip = Some(DiffOneTrip::new(
|
||||
|
@ -1,20 +1,21 @@
|
||||
mod associated;
|
||||
mod navigate;
|
||||
mod speed;
|
||||
mod time;
|
||||
mod turn_cycler;
|
||||
mod warp;
|
||||
|
||||
pub use self::speed::SpeedControls;
|
||||
pub use self::time::time_controls;
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::ID;
|
||||
use crate::render::DrawOptions;
|
||||
use crate::ui::UI;
|
||||
use abstutil::elapsed_seconds;
|
||||
use ezgui::{
|
||||
hotkey, Color, EventCtx, EventLoopMode, GfxCtx, HorizontalAlignment, Key, ModalMenu, MultiKey,
|
||||
ScreenPt, Slider, Text, VerticalAlignment,
|
||||
Text, VerticalAlignment,
|
||||
};
|
||||
use geom::Duration;
|
||||
use std::collections::BTreeSet;
|
||||
use std::time::Instant;
|
||||
|
||||
pub struct CommonState {
|
||||
associated: associated::ShowAssociatedState,
|
||||
@ -168,130 +169,3 @@ impl CommonState {
|
||||
opts
|
||||
}
|
||||
}
|
||||
|
||||
const ADJUST_SPEED: f64 = 0.1;
|
||||
// TODO hardcoded cap for now...
|
||||
const SPEED_CAP: f64 = 10.0 * 60.0;
|
||||
|
||||
pub struct SpeedControls {
|
||||
slider: Slider,
|
||||
state: State,
|
||||
}
|
||||
|
||||
enum State {
|
||||
Paused,
|
||||
Running {
|
||||
last_step: Instant,
|
||||
speed_description: String,
|
||||
last_measurement: Instant,
|
||||
last_measurement_sim: Duration,
|
||||
},
|
||||
}
|
||||
|
||||
impl SpeedControls {
|
||||
pub fn new(ctx: &mut EventCtx, top_left_at: Option<ScreenPt>) -> SpeedControls {
|
||||
let mut slider = Slider::new(top_left_at);
|
||||
slider.set_percent(ctx, 1.0 / SPEED_CAP);
|
||||
SpeedControls {
|
||||
slider,
|
||||
state: State::Paused,
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the amount of simulation time to step, if running.
|
||||
pub fn event(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
menu: &mut ModalMenu,
|
||||
current_sim_time: Duration,
|
||||
) -> Option<Duration> {
|
||||
let desired_speed = self.desired_speed();
|
||||
if desired_speed != SPEED_CAP && menu.action("speed up") {
|
||||
self.slider
|
||||
.set_percent(ctx, ((desired_speed + ADJUST_SPEED) / SPEED_CAP).min(1.0));
|
||||
} else if desired_speed != 0.0 && menu.action("slow down") {
|
||||
self.slider
|
||||
.set_percent(ctx, ((desired_speed - ADJUST_SPEED) / SPEED_CAP).max(0.0));
|
||||
} else if self.slider.event(ctx) {
|
||||
// Keep going
|
||||
}
|
||||
|
||||
match self.state {
|
||||
State::Paused => {
|
||||
if menu.action("pause/resume") {
|
||||
let now = Instant::now();
|
||||
self.state = State::Running {
|
||||
last_step: now,
|
||||
speed_description: "...".to_string(),
|
||||
last_measurement: now,
|
||||
last_measurement_sim: current_sim_time,
|
||||
};
|
||||
// Sorta hack to trigger EventLoopMode::Animation.
|
||||
return Some(Duration::ZERO);
|
||||
}
|
||||
}
|
||||
State::Running {
|
||||
ref mut last_step,
|
||||
ref mut speed_description,
|
||||
ref mut last_measurement,
|
||||
ref mut last_measurement_sim,
|
||||
} => {
|
||||
if menu.action("pause/resume") {
|
||||
self.state = State::Paused;
|
||||
} else if ctx.input.nonblocking_is_update_event() {
|
||||
ctx.input.use_update_event();
|
||||
let dt = Duration::seconds(elapsed_seconds(*last_step)) * desired_speed;
|
||||
*last_step = Instant::now();
|
||||
|
||||
let dt_descr = Duration::seconds(elapsed_seconds(*last_measurement));
|
||||
if dt_descr >= Duration::seconds(1.0) {
|
||||
*speed_description = format!(
|
||||
"{:.2}x",
|
||||
(current_sim_time - *last_measurement_sim) / dt_descr
|
||||
);
|
||||
*last_measurement = *last_step;
|
||||
*last_measurement_sim = current_sim_time;
|
||||
}
|
||||
return Some(dt);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx) {
|
||||
let mut txt = Text::new();
|
||||
if let State::Running {
|
||||
ref speed_description,
|
||||
..
|
||||
} = self.state
|
||||
{
|
||||
txt.add_line(format!(
|
||||
"Speed: {} / desired {:.2}x",
|
||||
speed_description,
|
||||
self.desired_speed()
|
||||
))
|
||||
} else {
|
||||
txt.add_line(format!(
|
||||
"Speed: paused / desired {:.2}x",
|
||||
self.desired_speed()
|
||||
))
|
||||
}
|
||||
self.slider.draw(g, Some(txt));
|
||||
}
|
||||
|
||||
pub fn pause(&mut self) {
|
||||
self.state = State::Paused;
|
||||
}
|
||||
|
||||
pub fn is_paused(&self) -> bool {
|
||||
match self.state {
|
||||
State::Paused => true,
|
||||
State::Running { .. } => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn desired_speed(&self) -> f64 {
|
||||
self.slider.get_percent() * SPEED_CAP
|
||||
}
|
||||
}
|
||||
|
131
editor/src/common/speed.rs
Normal file
131
editor/src/common/speed.rs
Normal file
@ -0,0 +1,131 @@
|
||||
use abstutil::elapsed_seconds;
|
||||
use ezgui::{EventCtx, GfxCtx, ModalMenu, ScreenPt, Slider, Text};
|
||||
use geom::Duration;
|
||||
use std::time::Instant;
|
||||
|
||||
const ADJUST_SPEED: f64 = 0.1;
|
||||
// TODO hardcoded cap for now...
|
||||
const SPEED_CAP: f64 = 10.0 * 60.0;
|
||||
|
||||
pub struct SpeedControls {
|
||||
slider: Slider,
|
||||
state: State,
|
||||
}
|
||||
|
||||
enum State {
|
||||
Paused,
|
||||
Running {
|
||||
last_step: Instant,
|
||||
speed_description: String,
|
||||
last_measurement: Instant,
|
||||
last_measurement_sim: Duration,
|
||||
},
|
||||
}
|
||||
|
||||
impl SpeedControls {
|
||||
pub fn new(ctx: &mut EventCtx, top_left_at: Option<ScreenPt>) -> SpeedControls {
|
||||
let mut slider = Slider::new(top_left_at);
|
||||
slider.set_percent(ctx, 1.0 / SPEED_CAP);
|
||||
SpeedControls {
|
||||
slider,
|
||||
state: State::Paused,
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the amount of simulation time to step, if running.
|
||||
pub fn event(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
menu: &mut ModalMenu,
|
||||
current_sim_time: Duration,
|
||||
) -> Option<Duration> {
|
||||
let desired_speed = self.desired_speed();
|
||||
if desired_speed != SPEED_CAP && menu.action("speed up") {
|
||||
self.slider
|
||||
.set_percent(ctx, ((desired_speed + ADJUST_SPEED) / SPEED_CAP).min(1.0));
|
||||
} else if desired_speed != 0.0 && menu.action("slow down") {
|
||||
self.slider
|
||||
.set_percent(ctx, ((desired_speed - ADJUST_SPEED) / SPEED_CAP).max(0.0));
|
||||
} else if self.slider.event(ctx) {
|
||||
// Keep going
|
||||
}
|
||||
|
||||
match self.state {
|
||||
State::Paused => {
|
||||
if menu.action("pause/resume") {
|
||||
let now = Instant::now();
|
||||
self.state = State::Running {
|
||||
last_step: now,
|
||||
speed_description: "...".to_string(),
|
||||
last_measurement: now,
|
||||
last_measurement_sim: current_sim_time,
|
||||
};
|
||||
// Sorta hack to trigger EventLoopMode::Animation.
|
||||
return Some(Duration::ZERO);
|
||||
}
|
||||
}
|
||||
State::Running {
|
||||
ref mut last_step,
|
||||
ref mut speed_description,
|
||||
ref mut last_measurement,
|
||||
ref mut last_measurement_sim,
|
||||
} => {
|
||||
if menu.action("pause/resume") {
|
||||
self.state = State::Paused;
|
||||
} else if ctx.input.nonblocking_is_update_event() {
|
||||
ctx.input.use_update_event();
|
||||
let dt = Duration::seconds(elapsed_seconds(*last_step)) * desired_speed;
|
||||
*last_step = Instant::now();
|
||||
|
||||
let dt_descr = Duration::seconds(elapsed_seconds(*last_measurement));
|
||||
if dt_descr >= Duration::seconds(1.0) {
|
||||
*speed_description = format!(
|
||||
"{:.2}x",
|
||||
(current_sim_time - *last_measurement_sim) / dt_descr
|
||||
);
|
||||
*last_measurement = *last_step;
|
||||
*last_measurement_sim = current_sim_time;
|
||||
}
|
||||
return Some(dt);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx) {
|
||||
let mut txt = Text::new();
|
||||
if let State::Running {
|
||||
ref speed_description,
|
||||
..
|
||||
} = self.state
|
||||
{
|
||||
txt.add_line(format!(
|
||||
"Speed: {} / desired {:.2}x",
|
||||
speed_description,
|
||||
self.desired_speed()
|
||||
))
|
||||
} else {
|
||||
txt.add_line(format!(
|
||||
"Speed: paused / desired {:.2}x",
|
||||
self.desired_speed()
|
||||
))
|
||||
}
|
||||
self.slider.draw(g, Some(txt));
|
||||
}
|
||||
|
||||
pub fn pause(&mut self) {
|
||||
self.state = State::Paused;
|
||||
}
|
||||
|
||||
pub fn is_paused(&self) -> bool {
|
||||
match self.state {
|
||||
State::Paused => true,
|
||||
State::Running { .. } => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn desired_speed(&self) -> f64 {
|
||||
self.slider.get_percent() * SPEED_CAP
|
||||
}
|
||||
}
|
70
editor/src/common/time.rs
Normal file
70
editor/src/common/time.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use crate::game::{State, Transition};
|
||||
use crate::mission::input_time;
|
||||
use crate::ui::UI;
|
||||
use ezgui::{EventCtx, GfxCtx, ModalMenu, Wizard};
|
||||
use geom::Duration;
|
||||
|
||||
pub fn time_controls(ctx: &mut EventCtx, ui: &mut UI, menu: &mut ModalMenu) -> Option<Transition> {
|
||||
if menu.action("step forwards 0.1s") {
|
||||
ui.primary.sim.step(&ui.primary.map, Duration::seconds(0.1));
|
||||
if let Some(ref mut s) = ui.secondary {
|
||||
s.sim.step(&s.map, Duration::seconds(0.1));
|
||||
}
|
||||
ui.recalculate_current_selection(ctx);
|
||||
} else if menu.action("step forwards 10 mins") {
|
||||
ctx.loading_screen("step forwards 10 minutes", |_, mut timer| {
|
||||
ui.primary
|
||||
.sim
|
||||
.timed_step(&ui.primary.map, Duration::minutes(10), &mut timer);
|
||||
if let Some(ref mut s) = ui.secondary {
|
||||
s.sim.timed_step(&s.map, Duration::minutes(10), &mut timer);
|
||||
}
|
||||
});
|
||||
ui.recalculate_current_selection(ctx);
|
||||
} else if menu.action("jump to specific time") {
|
||||
return Some(Transition::Push(Box::new(JumpingToTime {
|
||||
wizard: Wizard::new(),
|
||||
})));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
struct JumpingToTime {
|
||||
wizard: Wizard,
|
||||
}
|
||||
|
||||
impl State for JumpingToTime {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition {
|
||||
let mut wiz = self.wizard.wrap(ctx);
|
||||
|
||||
if let Some(t) = input_time(&mut wiz, "Jump to what time?") {
|
||||
let dt = t - ui.primary.sim.time();
|
||||
if dt <= Duration::ZERO {
|
||||
if wiz.acknowledge(
|
||||
"Bad time",
|
||||
vec![&format!("{} isn't after {}", t, ui.primary.sim.time())],
|
||||
) {
|
||||
return Transition::Pop;
|
||||
}
|
||||
} else {
|
||||
if dt > Duration::ZERO {
|
||||
ctx.loading_screen(&format!("step forwards {}", dt), |_, mut timer| {
|
||||
ui.primary.sim.timed_step(&ui.primary.map, dt, &mut timer);
|
||||
if let Some(ref mut s) = ui.secondary {
|
||||
s.sim.timed_step(&s.map, dt, &mut timer);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Transition::Pop;
|
||||
}
|
||||
} else if self.wizard.aborted() {
|
||||
return Transition::Pop;
|
||||
}
|
||||
Transition::Keep
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, _: &UI) {
|
||||
self.wizard.draw(g);
|
||||
}
|
||||
}
|
@ -5,13 +5,12 @@ mod show_activity;
|
||||
mod spawner;
|
||||
mod time_travel;
|
||||
|
||||
use crate::common::{CommonState, SpeedControls};
|
||||
use crate::common::{time_controls, CommonState, SpeedControls};
|
||||
use crate::debug::DebugMode;
|
||||
use crate::edit::EditMode;
|
||||
use crate::game::{State, Transition};
|
||||
use crate::mission::input_time;
|
||||
use crate::ui::{ShowEverything, UI};
|
||||
use ezgui::{hotkey, lctrl, EventCtx, EventLoopMode, GfxCtx, Key, ModalMenu, Text, Wizard};
|
||||
use ezgui::{hotkey, lctrl, EventCtx, EventLoopMode, GfxCtx, Key, ModalMenu, Text};
|
||||
use geom::Duration;
|
||||
use sim::{Sim, TripID};
|
||||
|
||||
@ -206,21 +205,10 @@ impl State for SandboxMode {
|
||||
}
|
||||
}
|
||||
|
||||
if self.menu.action("step forwards 0.1s") {
|
||||
ui.primary.sim.step(&ui.primary.map, Duration::seconds(0.1));
|
||||
ui.recalculate_current_selection(ctx);
|
||||
} else if self.menu.action("step forwards 10 mins") {
|
||||
ctx.loading_screen("step forwards 10 minutes", |_, mut timer| {
|
||||
ui.primary
|
||||
.sim
|
||||
.timed_step(&ui.primary.map, Duration::minutes(10), &mut timer);
|
||||
});
|
||||
ui.recalculate_current_selection(ctx);
|
||||
} else if self.menu.action("jump to specific time") {
|
||||
return Transition::Push(Box::new(JumpingToTime {
|
||||
wizard: Wizard::new(),
|
||||
}));
|
||||
if let Some(t) = time_controls(ctx, ui, &mut self.menu) {
|
||||
return t;
|
||||
}
|
||||
|
||||
Transition::Keep
|
||||
} else {
|
||||
Transition::KeepWithMode(EventLoopMode::Animation)
|
||||
@ -249,40 +237,3 @@ impl State for SandboxMode {
|
||||
self.speed.pause();
|
||||
}
|
||||
}
|
||||
|
||||
struct JumpingToTime {
|
||||
wizard: Wizard,
|
||||
}
|
||||
|
||||
impl State for JumpingToTime {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition {
|
||||
let mut wiz = self.wizard.wrap(ctx);
|
||||
|
||||
if let Some(t) = input_time(&mut wiz, "Jump to what time?") {
|
||||
let dt = t - ui.primary.sim.time();
|
||||
if dt <= Duration::ZERO {
|
||||
if wiz.acknowledge(
|
||||
"Bad time",
|
||||
vec![&format!("{} isn't after {}", t, ui.primary.sim.time())],
|
||||
) {
|
||||
return Transition::Pop;
|
||||
}
|
||||
} else {
|
||||
if dt > Duration::ZERO {
|
||||
ctx.loading_screen(&format!("step forwards {}", dt), |_, mut timer| {
|
||||
ui.primary.sim.timed_step(&ui.primary.map, dt, &mut timer);
|
||||
});
|
||||
}
|
||||
|
||||
return Transition::Pop;
|
||||
}
|
||||
} else if self.wizard.aborted() {
|
||||
return Transition::Pop;
|
||||
}
|
||||
Transition::Keep
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, _: &UI) {
|
||||
self.wizard.draw(g);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user