diff --git a/game/src/challenges.rs b/game/src/challenges.rs new file mode 100644 index 0000000000..a1fc4bdf2d --- /dev/null +++ b/game/src/challenges.rs @@ -0,0 +1,91 @@ +use crate::game::{State, Transition, WizardState}; +use crate::ui::UI; +use ezgui::{ + hotkey, Choice, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, ModalMenu, Text, + VerticalAlignment, +}; + +// TODO Also have some kind of screenshot to display for each challenge +#[derive(Clone)] +struct Challenge { + title: String, + description: String, + map_name: String, + scenario_name: String, +} +impl abstutil::Cloneable for Challenge {} + +fn all_challenges() -> Vec { + vec![ + Challenge { + title: "Speed up route 980".to_string(), + description: + "Decrease the average waiting time between all of 980's stops by at least 30s" + .to_string(), + map_name: "montlake".to_string(), + scenario_name: "weekday_typical_traffic_from_psrc".to_string(), + }, + Challenge { + title: "Speed up route 27 along Yesler".to_string(), + description: + "Decrease the average waiting time between all of 27's stops by at least 30s" + .to_string(), + map_name: "23rd".to_string(), + scenario_name: "weekday_typical_traffic_from_psrc".to_string(), + }, + ] +} + +pub fn challenges_picker() -> Box { + WizardState::new(Box::new(move |wiz, ctx, _| { + let (_, challenge) = wiz.wrap(ctx).choose("Play which challenge?", || { + all_challenges() + .into_iter() + .map(|c| Choice::new(c.title.clone(), c)) + .collect() + })?; + + let mut summary = Text::from(Line(&challenge.description)); + summary.add(Line("")); + summary.add(Line("Proposals:")); + summary.add(Line("")); + summary.add(Line("- bus lane fix (untested)")); + summary.add(Line("- signal retiming (score 500)")); + + Some(Transition::Replace(Box::new(ChallengeSplash { + summary, + menu: ModalMenu::new( + &challenge.title, + vec![ + (hotkey(Key::Escape), "back to challenges"), + (hotkey(Key::S), "start challenge"), + (hotkey(Key::L), "load existing proposal"), + ], + ctx, + ), + }))) + })) +} + +struct ChallengeSplash { + menu: ModalMenu, + summary: Text, +} + +impl State for ChallengeSplash { + fn event(&mut self, ctx: &mut EventCtx, _: &mut UI) -> Transition { + self.menu.event(ctx); + if self.menu.action("back to challenges") { + return Transition::Replace(challenges_picker()); + } + Transition::Keep + } + + fn draw(&self, g: &mut GfxCtx, _: &UI) { + g.draw_blocking_text( + &self.summary, + (HorizontalAlignment::Center, VerticalAlignment::Center), + ); + self.menu.draw(g); + } +} diff --git a/game/src/main.rs b/game/src/main.rs index 363f9ddf65..fbab5e0b82 100644 --- a/game/src/main.rs +++ b/game/src/main.rs @@ -1,4 +1,5 @@ mod abtest; +mod challenges; mod common; mod debug; mod edit; diff --git a/game/src/splash_screen.rs b/game/src/splash_screen.rs index 6500597b11..c998fc14df 100644 --- a/game/src/splash_screen.rs +++ b/game/src/splash_screen.rs @@ -1,4 +1,5 @@ use crate::abtest::setup::PickABTest; +use crate::challenges::challenges_picker; use crate::debug::DebugMode; use crate::edit::EditMode; use crate::game::{State, Transition}; @@ -123,6 +124,7 @@ fn splash_screen( ) -> Option { let mut wizard = raw_wizard.wrap(ctx); let sandbox = "Sandbox mode"; + let challenge = "Challenge mode"; let load_map = "Load another map"; let edit = "Edit map"; let abtest = "A/B Test Mode"; @@ -142,6 +144,7 @@ fn splash_screen( .choose("Welcome to A/B Street!", || { vec![ Choice::new(sandbox, ()).key(Key::S), + Choice::new(challenge, ()).key(Key::C), Choice::new(load_map, ()).key(Key::L), Choice::new(edit, ()).key(Key::E), Choice::new(abtest, ()).key(Key::A), @@ -156,6 +159,7 @@ fn splash_screen( .as_str() { x if x == sandbox => Some(Transition::Push(Box::new(SandboxMode::new(ctx, ui)))), + x if x == challenge => Some(Transition::Push(challenges_picker())), x if x == load_map => { if let Some(name) = wizard.choose_string("Load which map?", || { let current_map = ui.primary.map.get_name();