more easy WizardState conversion

This commit is contained in:
Dustin Carlino 2019-08-07 12:55:16 -07:00
parent 136ca2d7ab
commit f26ab131e6
5 changed files with 121 additions and 201 deletions

View File

@ -1,52 +1,71 @@
use crate::abtest::{ABTestMode, ABTestSavestate};
use crate::edit::apply_map_edits;
use crate::game::{State, Transition};
use crate::game::{State, Transition, WizardState};
use crate::render::DrawMap;
use crate::ui::{Flags, PerMapUI, UI};
use ezgui::{hotkey, EventCtx, GfxCtx, Key, LogScroller, ModalMenu, Wizard, WrappedWizard};
use geom::Duration;
use map_model::{Map, MapEdits};
use map_model::MapEdits;
use sim::{ABTest, Scenario, SimFlags};
use std::path::PathBuf;
pub struct PickABTest {
wizard: Wizard,
}
pub struct PickABTest;
impl PickABTest {
pub fn new() -> PickABTest {
PickABTest {
wizard: Wizard::new(),
}
pub fn new() -> Box<State> {
WizardState::new(Box::new(pick_ab_test))
}
}
impl State for PickABTest {
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition {
if let Some(ab_test) = pick_ab_test(&ui.primary.map, self.wizard.wrap(ctx)) {
let scroller = LogScroller::new(ab_test.test_name.clone(), ab_test.describe());
return Transition::Replace(Box::new(ABTestSetup {
menu: ModalMenu::new(
&format!("A/B Test Editor for {}", ab_test.test_name),
vec![vec![
(hotkey(Key::Escape), "quit"),
(hotkey(Key::R), "run A/B test"),
(hotkey(Key::L), "load savestate"),
]],
ctx,
),
ab_test,
scroller,
}));
} else if self.wizard.aborted() {
return Transition::Pop;
}
Transition::Keep
}
fn pick_ab_test(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<Transition> {
let mut wizard = wiz.wrap(ctx);
let load_existing = "Load existing A/B test";
let create_new = "Create new A/B test";
let map_name = ui.primary.map.get_name().to_string();
let ab_test = if wizard
.choose_string("What A/B test to manage?", vec![load_existing, create_new])?
== load_existing
{
wizard
.choose_something_no_keys::<ABTest>(
"Load which A/B test?",
Box::new(move || abstutil::load_all_objects(abstutil::AB_TESTS, &map_name)),
)?
.1
} else {
let test_name = wizard.input_string("Name the A/B test")?;
let t = ABTest {
test_name,
map_name: map_name.clone(),
scenario_name: choose_scenario(map_name.clone(), &mut wizard, "What scenario to run?")?,
edits1_name: choose_edits(
map_name.clone(),
&mut wizard,
"For the 1st run, what map edits to use?",
)?,
edits2_name: choose_edits(
map_name.clone(),
&mut wizard,
"For the 2nd run, what map edits to use?",
)?,
};
t.save();
t
};
fn draw(&self, g: &mut GfxCtx, _: &UI) {
self.wizard.draw(g);
}
let scroller = LogScroller::new(ab_test.test_name.clone(), ab_test.describe());
Some(Transition::Replace(Box::new(ABTestSetup {
menu: ModalMenu::new(
&format!("A/B Test Editor for {}", ab_test.test_name),
vec![vec![
(hotkey(Key::Escape), "quit"),
(hotkey(Key::R), "run A/B test"),
(hotkey(Key::L), "load savestate"),
]],
ctx,
),
ab_test,
scroller,
})))
}
struct ABTestSetup {
@ -98,27 +117,6 @@ impl State for LoadSavestate {
}
}
fn pick_ab_test(map: &Map, mut wizard: WrappedWizard) -> Option<ABTest> {
let load_existing = "Load existing A/B test";
let create_new = "Create new A/B test";
if wizard.choose_string("What A/B test to manage?", vec![load_existing, create_new])?
== load_existing
{
load_ab_test(map, &mut wizard, "Load which A/B test?")
} else {
let test_name = wizard.input_string("Name the A/B test")?;
let ab_test = ABTest {
test_name,
map_name: map.get_name().to_string(),
scenario_name: choose_scenario(map, &mut wizard, "What scenario to run?")?,
edits1_name: choose_edits(map, &mut wizard, "For the 1st run, what map edits to use?")?,
edits2_name: choose_edits(map, &mut wizard, "For the 2nd run, what map edits to use?")?,
};
ab_test.save();
Some(ab_test)
}
}
fn launch_test(test: &ABTest, ui: &mut UI, ctx: &mut EventCtx) -> ABTestMode {
let secondary = ctx.loading_screen(
&format!("Launching A/B test {}", test.test_name),
@ -234,8 +232,7 @@ fn launch_savestate(test: &ABTest, ss_path: String, ui: &mut UI, ctx: &mut Event
)
}
fn choose_scenario(map: &Map, wizard: &mut WrappedWizard, query: &str) -> Option<String> {
let map_name = map.get_name().to_string();
fn choose_scenario(map_name: String, wizard: &mut WrappedWizard, query: &str) -> Option<String> {
wizard
.choose_something_no_keys::<String>(
query,
@ -244,8 +241,7 @@ fn choose_scenario(map: &Map, wizard: &mut WrappedWizard, query: &str) -> Option
.map(|(n, _)| n)
}
fn choose_edits(map: &Map, wizard: &mut WrappedWizard, query: &str) -> Option<String> {
let map_name = map.get_name().to_string();
fn choose_edits(map_name: String, wizard: &mut WrappedWizard, query: &str) -> Option<String> {
wizard
.choose_something_no_keys::<String>(
query,
@ -258,16 +254,6 @@ fn choose_edits(map: &Map, wizard: &mut WrappedWizard, query: &str) -> Option<St
.map(|(n, _)| n)
}
fn load_ab_test(map: &Map, wizard: &mut WrappedWizard, query: &str) -> Option<ABTest> {
let map_name = map.get_name().to_string();
wizard
.choose_something_no_keys::<ABTest>(
query,
Box::new(move || abstutil::load_all_objects(abstutil::AB_TESTS, &map_name)),
)
.map(|(_, t)| t)
}
fn pick_savestate(test: &ABTest, wizard: &mut WrappedWizard) -> Option<String> {
let path = abstutil::path1(&test.map_name, abstutil::AB_TEST_SAVES, &test.test_name);
wizard

View File

@ -1,6 +1,6 @@
use crate::game::{State, Transition};
use crate::game::{Transition, WizardState};
use crate::ui::UI;
use ezgui::{EventCtx, GfxCtx, ModalMenu, Wizard};
use ezgui::{EventCtx, ModalMenu, Wizard};
use geom::Duration;
pub fn time_controls(ctx: &mut EventCtx, ui: &mut UI, menu: &mut ModalMenu) -> Option<Transition> {
@ -21,41 +21,23 @@ pub fn time_controls(ctx: &mut EventCtx, ui: &mut UI, menu: &mut ModalMenu) -> O
});
ui.recalculate_current_selection(ctx);
} else if menu.action("jump to specific time") {
return Some(Transition::Push(Box::new(JumpingToTime {
wizard: Wizard::new(),
})));
return Some(Transition::Push(WizardState::new(Box::new(jump_to_time))));
}
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) = wiz.input_time_slider(
"Jump to what time?",
ui.primary.sim.time(),
Duration::END_OF_DAY,
) {
let dt = t - ui.primary.sim.time();
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;
fn jump_to_time(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<Transition> {
let t = wiz.wrap(ctx).input_time_slider(
"Jump to what time?",
ui.primary.sim.time(),
Duration::END_OF_DAY,
)?;
let dt = t - ui.primary.sim.time();
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);
}
Transition::Keep
}
fn draw(&self, g: &mut GfxCtx, _: &UI) {
self.wizard.draw(g);
}
});
Some(Transition::Pop)
}

View File

@ -4,13 +4,12 @@ mod individ_trips;
mod neighborhood;
mod scenario;
use crate::game::{State, Transition};
use crate::game::{State, Transition, WizardState};
use crate::sandbox::SandboxMode;
use crate::ui::UI;
use abstutil::Timer;
use ezgui::{hotkey, EventCtx, GfxCtx, Key, ModalMenu, Wizard, WrappedWizard};
use geom::Duration;
use map_model::Map;
use popdat::trips_to_scenario;
use sim::Scenario;
@ -80,19 +79,13 @@ impl State for MissionEditMode {
});
return Transition::Replace(Box::new(SandboxMode::new(ctx)));
} else if self.menu.action("create scenario from PSRC trips") {
return Transition::Push(Box::new(TripsToScenario {
wizard: Wizard::new(),
}));
return Transition::Push(WizardState::new(Box::new(convert_trips_to_scenario)));
} else if self.menu.action("manage neighborhoods") {
return Transition::Push(Box::new(neighborhood::NeighborhoodPicker::new()));
} else if self.menu.action("load scenario") {
return Transition::Push(Box::new(LoadScenario {
wizard: Wizard::new(),
}));
return Transition::Push(WizardState::new(Box::new(load_scenario)));
} else if self.menu.action("create new scenario") {
return Transition::Push(Box::new(CreateNewScenario {
wizard: Wizard::new(),
}));
return Transition::Push(WizardState::new(Box::new(create_new_scenario)));
}
Transition::Keep
}
@ -102,79 +95,53 @@ impl State for MissionEditMode {
}
}
struct TripsToScenario {
wizard: Wizard,
fn convert_trips_to_scenario(
wiz: &mut Wizard,
ctx: &mut EventCtx,
ui: &mut UI,
) -> Option<Transition> {
let (t1, t2) = pick_time_range(
&mut wiz.wrap(ctx),
"Include trips departing AFTER when?",
"Include trips departing BEFORE when?",
)?;
ctx.loading_screen("extract PSRC scenario", |_, mut timer| {
trips_to_scenario(&ui.primary.map, t1, t2, &mut timer).save();
});
Some(Transition::Pop)
}
impl State for TripsToScenario {
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition {
if let Some((t1, t2)) = pick_time_range(
&mut self.wizard.wrap(ctx),
"Include trips departing AFTER when?",
"Include trips departing BEFORE when?",
) {
ctx.loading_screen("extract PSRC scenario", |_, mut timer| {
trips_to_scenario(&ui.primary.map, t1, t2, &mut timer).save();
});
return Transition::Pop;
} else if self.wizard.aborted() {
return Transition::Pop;
}
Transition::Keep
}
fn draw(&self, g: &mut GfxCtx, _: &UI) {
self.wizard.draw(g);
}
fn load_scenario(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<Transition> {
let map_name = ui.primary.map.get_name().to_string();
let (_, s) = wiz.wrap(ctx).choose_something_no_keys::<String>(
"Load which scenario?",
Box::new(move || abstutil::list_all_objects(abstutil::SCENARIOS, &map_name)),
)?;
let scenario = abstutil::read_binary(
&abstutil::path1_bin(ui.primary.map.get_name(), abstutil::SCENARIOS, &s),
&mut Timer::throwaway(),
)
.unwrap();
Some(Transition::Replace(Box::new(
scenario::ScenarioManager::new(scenario, ctx),
)))
}
struct LoadScenario {
wizard: Wizard,
}
impl State for LoadScenario {
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition {
if let Some(scenario) = load_scenario(&ui.primary.map, &mut self.wizard.wrap(ctx)) {
return Transition::Replace(Box::new(scenario::ScenarioManager::new(scenario, ctx)));
} else if self.wizard.aborted() {
return Transition::Pop;
}
Transition::Keep
}
fn draw(&self, g: &mut GfxCtx, _: &UI) {
self.wizard.draw(g);
}
}
struct CreateNewScenario {
wizard: Wizard,
}
impl State for CreateNewScenario {
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition {
let mut wrapped = self.wizard.wrap(ctx);
if let Some(name) = wrapped.input_string("Name the scenario") {
return Transition::Replace(Box::new(scenario::ScenarioManager::new(
Scenario {
scenario_name: name,
map_name: ui.primary.map.get_name().to_string(),
seed_parked_cars: Vec::new(),
spawn_over_time: Vec::new(),
border_spawn_over_time: Vec::new(),
individ_trips: Vec::new(),
},
ctx,
)));
} else if self.wizard.aborted() {
return Transition::Pop;
}
Transition::Keep
}
fn draw(&self, g: &mut GfxCtx, _: &UI) {
self.wizard.draw(g);
}
fn create_new_scenario(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<Transition> {
let name = wiz.wrap(ctx).input_string("Name the scenario")?;
Some(Transition::Replace(Box::new(
scenario::ScenarioManager::new(
Scenario {
scenario_name: name,
map_name: ui.primary.map.get_name().to_string(),
seed_parked_cars: Vec::new(),
spawn_over_time: Vec::new(),
border_spawn_over_time: Vec::new(),
individ_trips: Vec::new(),
},
ctx,
),
)))
}
pub fn pick_time_range(
@ -186,19 +153,3 @@ pub fn pick_time_range(
let t2 = wizard.input_time_slider(high_query, t1, Duration::END_OF_DAY)?;
Some((t1, t2))
}
fn load_scenario(map: &Map, wizard: &mut WrappedWizard) -> Option<Scenario> {
let map_name = map.get_name().to_string();
wizard
.choose_something_no_keys::<String>(
"Load which scenario?",
Box::new(move || abstutil::list_all_objects(abstutil::SCENARIOS, &map_name)),
)
.map(|(_, s)| {
abstutil::read_binary(
&abstutil::path1_bin(map.get_name(), abstutil::SCENARIOS, &s),
&mut Timer::throwaway(),
)
.unwrap()
})
}

View File

@ -6,6 +6,7 @@ use map_model::{Map, NeighborhoodBuilder};
const POINT_RADIUS: Distance = Distance::const_meters(10.0);
// This shouldn't get subsumed by WizardState, since it has such an interesting draw().
pub struct NeighborhoodPicker {
wizard: Wizard,
}

View File

@ -192,7 +192,7 @@ fn splash_screen(
x if x == tutorial => Some(Transition::Push(Box::new(TutorialMode::new(ctx, ui)))),
x if x == debug => Some(Transition::Push(Box::new(DebugMode::new(ctx, ui)))),
x if x == mission => Some(Transition::Push(Box::new(MissionEditMode::new(ctx, ui)))),
x if x == abtest => Some(Transition::Push(Box::new(PickABTest::new()))),
x if x == abtest => Some(Transition::Push(PickABTest::new())),
x if x == about => {
if wizard.acknowledge(
"About A/B Street",