mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-11 08:15:34 +03:00
make a second part to the optimize commute challenge. just optimize two separate people. [rebuild]
This commit is contained in:
parent
5cf0b72bac
commit
7bf899cab8
@ -13,7 +13,7 @@ use geom::{Bounds, Circle, Distance, Pt2D};
|
||||
use map_model::{Map, Traversable};
|
||||
use rand::seq::SliceRandom;
|
||||
use sim::{Analytics, GetDrawAgents, Sim, SimFlags};
|
||||
use std::collections::HashMap;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub struct App {
|
||||
pub primary: PerMap,
|
||||
@ -499,14 +499,14 @@ impl PerMap {
|
||||
// TODO Serialize these, but in a very careful, future-compatible way
|
||||
pub struct SessionState {
|
||||
pub tutorial: Option<TutorialState>,
|
||||
pub high_scores: HashMap<GameplayMode, Vec<HighScore>>,
|
||||
pub high_scores: BTreeMap<GameplayMode, Vec<HighScore>>,
|
||||
}
|
||||
|
||||
impl SessionState {
|
||||
pub fn empty() -> SessionState {
|
||||
SessionState {
|
||||
tutorial: None,
|
||||
high_scores: HashMap::new(),
|
||||
high_scores: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ pub struct Challenge {
|
||||
pub description: Vec<String>,
|
||||
pub alias: String,
|
||||
pub gameplay: GameplayMode,
|
||||
cutscene: Option<fn(&mut EventCtx, &App) -> Box<dyn State>>,
|
||||
pub cutscene: Option<fn(&mut EventCtx, &App) -> Box<dyn State>>,
|
||||
}
|
||||
|
||||
// TODO Assuming the measurement is always maximizing time savings from a goal.
|
||||
@ -25,69 +25,105 @@ pub struct HighScore {
|
||||
pub edits_name: String,
|
||||
}
|
||||
|
||||
pub fn all_challenges(dev: bool) -> BTreeMap<String, Vec<Challenge>> {
|
||||
let mut tree = BTreeMap::new();
|
||||
tree.insert(
|
||||
"Optimize one commute".to_string(),
|
||||
vec![Challenge {
|
||||
title: "Part 1".to_string(),
|
||||
description: vec!["Speed up one VIP's daily commute, at any cost!".to_string()],
|
||||
alias: "commute/pt1".to_string(),
|
||||
gameplay: GameplayMode::OptimizeCommute(PersonID(3434)),
|
||||
cutscene: Some(crate::sandbox::gameplay::commute::OptimizeCommute::cutscene),
|
||||
}],
|
||||
);
|
||||
|
||||
if dev {
|
||||
impl Challenge {
|
||||
pub fn all(dev: bool) -> BTreeMap<String, Vec<Challenge>> {
|
||||
let mut tree = BTreeMap::new();
|
||||
tree.insert(
|
||||
"Fix traffic signals".to_string(),
|
||||
"Optimize one commute".to_string(),
|
||||
vec![
|
||||
Challenge {
|
||||
title: "Tutorial 1".to_string(),
|
||||
description: vec!["Add or remove a dedicated left phase".to_string()],
|
||||
alias: "trafficsig/tut1".to_string(),
|
||||
gameplay: GameplayMode::FixTrafficSignalsTutorial(0),
|
||||
cutscene: None,
|
||||
title: "Part 1".to_string(),
|
||||
description: vec!["Speed up one VIP's daily commute, at any cost!".to_string()],
|
||||
alias: "commute/pt1".to_string(),
|
||||
gameplay: GameplayMode::OptimizeCommute(PersonID(1163), Duration::minutes(2)),
|
||||
cutscene: Some(
|
||||
crate::sandbox::gameplay::commute::OptimizeCommute::cutscene_pt1,
|
||||
),
|
||||
},
|
||||
Challenge {
|
||||
title: "Tutorial 2".to_string(),
|
||||
description: vec!["Deal with heavy foot traffic".to_string()],
|
||||
alias: "trafficsig/tut2".to_string(),
|
||||
gameplay: GameplayMode::FixTrafficSignalsTutorial(1),
|
||||
cutscene: None,
|
||||
},
|
||||
Challenge {
|
||||
title: "The real challenge!".to_string(),
|
||||
description: vec![
|
||||
"A city-wide power surge knocked out all of the traffic signals!"
|
||||
.to_string(),
|
||||
"Their timing has been reset to default settings, and drivers are stuck."
|
||||
.to_string(),
|
||||
"It's up to you to repair the signals, choosing appropriate turn phases \
|
||||
and timing."
|
||||
.to_string(),
|
||||
"".to_string(),
|
||||
"Objective: Reduce the average trip time by at least 30s".to_string(),
|
||||
],
|
||||
alias: "trafficsig/main".to_string(),
|
||||
gameplay: GameplayMode::FixTrafficSignals,
|
||||
cutscene: None,
|
||||
title: "Part 2".to_string(),
|
||||
description: vec!["Speed up another VIP's commute".to_string()],
|
||||
alias: "commute/pt2".to_string(),
|
||||
gameplay: GameplayMode::OptimizeCommute(PersonID(3434), Duration::minutes(5)),
|
||||
cutscene: Some(
|
||||
crate::sandbox::gameplay::commute::OptimizeCommute::cutscene_pt2,
|
||||
),
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
tree.insert(
|
||||
"Cause gridlock (WIP)".to_string(),
|
||||
vec![Challenge {
|
||||
title: "Gridlock all of the everything".to_string(),
|
||||
description: vec!["Make traffic as BAD as possible!".to_string()],
|
||||
alias: "gridlock".to_string(),
|
||||
gameplay: GameplayMode::CreateGridlock(abstutil::path_map("montlake")),
|
||||
cutscene: None,
|
||||
}],
|
||||
);
|
||||
if dev {
|
||||
tree.insert(
|
||||
"Fix traffic signals".to_string(),
|
||||
vec![
|
||||
Challenge {
|
||||
title: "Tutorial 1".to_string(),
|
||||
description: vec!["Add or remove a dedicated left phase".to_string()],
|
||||
alias: "trafficsig/tut1".to_string(),
|
||||
gameplay: GameplayMode::FixTrafficSignalsTutorial(0),
|
||||
cutscene: None,
|
||||
},
|
||||
Challenge {
|
||||
title: "Tutorial 2".to_string(),
|
||||
description: vec!["Deal with heavy foot traffic".to_string()],
|
||||
alias: "trafficsig/tut2".to_string(),
|
||||
gameplay: GameplayMode::FixTrafficSignalsTutorial(1),
|
||||
cutscene: None,
|
||||
},
|
||||
Challenge {
|
||||
title: "The real challenge!".to_string(),
|
||||
description: vec![
|
||||
"A city-wide power surge knocked out all of the traffic signals!"
|
||||
.to_string(),
|
||||
"Their timing has been reset to default settings, and drivers are \
|
||||
stuck."
|
||||
.to_string(),
|
||||
"It's up to you to repair the signals, choosing appropriate turn \
|
||||
phases and timing."
|
||||
.to_string(),
|
||||
"".to_string(),
|
||||
"Objective: Reduce the average trip time by at least 30s".to_string(),
|
||||
],
|
||||
alias: "trafficsig/main".to_string(),
|
||||
gameplay: GameplayMode::FixTrafficSignals,
|
||||
cutscene: None,
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
tree.insert(
|
||||
"Cause gridlock (WIP)".to_string(),
|
||||
vec![Challenge {
|
||||
title: "Gridlock all of the everything".to_string(),
|
||||
description: vec!["Make traffic as BAD as possible!".to_string()],
|
||||
alias: "gridlock".to_string(),
|
||||
gameplay: GameplayMode::CreateGridlock(abstutil::path_map("montlake")),
|
||||
cutscene: None,
|
||||
}],
|
||||
);
|
||||
}
|
||||
tree
|
||||
}
|
||||
|
||||
// Also returns the next stage, if there is one
|
||||
pub fn find(mode: &GameplayMode) -> (Challenge, Option<Challenge>) {
|
||||
// Find the next stage
|
||||
for (_, stages) in Challenge::all(true) {
|
||||
let mut current = None;
|
||||
for challenge in stages {
|
||||
if current.is_some() {
|
||||
return (current.unwrap(), Some(challenge));
|
||||
}
|
||||
if &challenge.gameplay == mode {
|
||||
current = Some(challenge);
|
||||
}
|
||||
}
|
||||
if let Some(c) = current {
|
||||
return (c, None);
|
||||
}
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
tree
|
||||
}
|
||||
|
||||
pub fn challenges_picker(ctx: &mut EventCtx, app: &mut App) -> Box<dyn State> {
|
||||
@ -117,7 +153,7 @@ impl Tab {
|
||||
|
||||
// First list challenges
|
||||
let mut flex_row = Vec::new();
|
||||
for (idx, (name, _)) in all_challenges(app.opts.dev).into_iter().enumerate() {
|
||||
for (idx, (name, _)) in Challenge::all(app.opts.dev).into_iter().enumerate() {
|
||||
let current = match self {
|
||||
Tab::NothingChosen => false,
|
||||
Tab::ChallengeStage(ref n, _) => &name == n,
|
||||
@ -154,7 +190,7 @@ impl Tab {
|
||||
// List stages
|
||||
if let Tab::ChallengeStage(ref name, current) = self {
|
||||
let mut col = Vec::new();
|
||||
for (idx, stage) in all_challenges(app.opts.dev)
|
||||
for (idx, stage) in Challenge::all(app.opts.dev)
|
||||
.remove(name)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
@ -186,7 +222,7 @@ impl Tab {
|
||||
|
||||
// Describe the specific stage
|
||||
if let Tab::ChallengeStage(ref name, current) = self {
|
||||
let challenge = all_challenges(app.opts.dev)
|
||||
let challenge = Challenge::all(app.opts.dev)
|
||||
.remove(name)
|
||||
.unwrap()
|
||||
.remove(current);
|
||||
@ -257,7 +293,7 @@ pub fn prebake_all() {
|
||||
let mut timer = Timer::new("prebake all challenge results");
|
||||
|
||||
let mut per_map: BTreeMap<String, Vec<Challenge>> = BTreeMap::new();
|
||||
for (_, list) in all_challenges(true) {
|
||||
for (_, list) in Challenge::all(true) {
|
||||
for c in list {
|
||||
per_map
|
||||
.entry(c.gameplay.map_path())
|
||||
|
@ -83,7 +83,7 @@ fn main() {
|
||||
let mut mode = None;
|
||||
if let Some(x) = args.optional("--challenge") {
|
||||
let mut aliases = Vec::new();
|
||||
'OUTER: for (_, stages) in challenges::all_challenges(true) {
|
||||
'OUTER: for (_, stages) in challenges::Challenge::all(true) {
|
||||
for challenge in stages {
|
||||
if challenge.alias == x {
|
||||
flags.sim_flags.load = challenge.gameplay.map_path();
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::app::App;
|
||||
use crate::challenges::{challenges_picker, HighScore};
|
||||
use crate::challenges::{challenges_picker, Challenge, HighScore};
|
||||
use crate::common::{ContextualActions, Tab};
|
||||
use crate::cutscene::CutsceneBuilder;
|
||||
use crate::edit::EditMode;
|
||||
@ -19,11 +19,10 @@ use std::collections::BTreeMap;
|
||||
|
||||
// TODO A nice level to unlock: specifying your own commute, getting to work on it
|
||||
|
||||
const GOAL: Duration = Duration::const_seconds(3.0 * 60.0);
|
||||
|
||||
pub struct OptimizeCommute {
|
||||
top_center: Composite,
|
||||
person: PersonID,
|
||||
goal: Duration,
|
||||
time: Time,
|
||||
|
||||
// Cache here for convenience
|
||||
@ -33,18 +32,32 @@ pub struct OptimizeCommute {
|
||||
}
|
||||
|
||||
impl OptimizeCommute {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App, person: PersonID) -> Box<dyn GameplayState> {
|
||||
pub fn new(
|
||||
ctx: &mut EventCtx,
|
||||
app: &App,
|
||||
person: PersonID,
|
||||
goal: Duration,
|
||||
) -> Box<dyn GameplayState> {
|
||||
let trips = app.primary.sim.get_person(person).trips.clone();
|
||||
Box::new(OptimizeCommute {
|
||||
top_center: make_top_center(ctx, app, Duration::ZERO, Duration::ZERO, 0, trips.len()),
|
||||
top_center: make_top_center(
|
||||
ctx,
|
||||
app,
|
||||
Duration::ZERO,
|
||||
Duration::ZERO,
|
||||
0,
|
||||
trips.len(),
|
||||
goal,
|
||||
),
|
||||
person,
|
||||
goal,
|
||||
time: Time::START_OF_DAY,
|
||||
trips,
|
||||
once: true,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn cutscene(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
pub fn cutscene_pt1(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
CutsceneBuilder::new()
|
||||
.scene("boss", "Listen up, I've got a special job for you today.")
|
||||
.scene(
|
||||
@ -70,7 +83,7 @@ impl OptimizeCommute {
|
||||
.scene(
|
||||
"boss",
|
||||
"That's none of your concern! I've anonymized their name, so don't even bother \
|
||||
digging into what happened at dinn --",
|
||||
digging into what happened in Ballard --",
|
||||
)
|
||||
.scene("boss", "JUST GET TO WORK, KID!")
|
||||
.narrator(
|
||||
@ -83,7 +96,40 @@ impl OptimizeCommute {
|
||||
)
|
||||
.narrator(
|
||||
"Ignore the damage done to everyone else. Just speed up the VIP's trips by a \
|
||||
total of 3 minutes.",
|
||||
total of 2 minutes.",
|
||||
)
|
||||
.build(ctx, app)
|
||||
}
|
||||
|
||||
pub fn cutscene_pt2(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
CutsceneBuilder::new()
|
||||
.scene(
|
||||
"boss",
|
||||
"I've got another, er, friend who's sick of this parking situation.",
|
||||
)
|
||||
.scene(
|
||||
"player",
|
||||
"Yeah, why do we dedicate so much valuable land to storing unused cars? It's \
|
||||
ridiculous!",
|
||||
)
|
||||
.scene(
|
||||
"boss",
|
||||
"No, I mean, they're tired of having to hunt for parking. You need to make it \
|
||||
easier.",
|
||||
)
|
||||
.scene(
|
||||
"player",
|
||||
"What? We're trying to encourage people to be less car-dependent. Why's this \
|
||||
\"friend\" more important than the city's carbon-neutral goals?",
|
||||
)
|
||||
.scene(
|
||||
"boss",
|
||||
"Everyone's calling in favors these days. Just make it happen!",
|
||||
)
|
||||
.narrator("Too many people have dirt on the boss. Guess we have another VIP to help.")
|
||||
.narrator(
|
||||
"Once again, ignore the damage to everyone else, and just speed up the VIP's \
|
||||
trips by a total of 5 minutes.",
|
||||
)
|
||||
.build(ctx, app)
|
||||
}
|
||||
@ -112,16 +158,18 @@ impl GameplayState for OptimizeCommute {
|
||||
self.time = app.primary.sim.time();
|
||||
|
||||
let (before, after, done) = get_score(app, &self.trips);
|
||||
self.top_center = make_top_center(ctx, app, before, after, done, self.trips.len());
|
||||
self.top_center =
|
||||
make_top_center(ctx, app, before, after, done, self.trips.len(), self.goal);
|
||||
|
||||
if done == self.trips.len() {
|
||||
return (
|
||||
Some(final_score(
|
||||
ctx,
|
||||
app,
|
||||
GameplayMode::OptimizeCommute(self.person),
|
||||
GameplayMode::OptimizeCommute(self.person, self.goal),
|
||||
before,
|
||||
after,
|
||||
self.goal,
|
||||
)),
|
||||
false,
|
||||
);
|
||||
@ -135,14 +183,21 @@ impl GameplayState for OptimizeCommute {
|
||||
Some(Transition::Push(Box::new(EditMode::new(
|
||||
ctx,
|
||||
app,
|
||||
GameplayMode::OptimizeCommute(self.person),
|
||||
GameplayMode::OptimizeCommute(self.person, self.goal),
|
||||
)))),
|
||||
false,
|
||||
);
|
||||
}
|
||||
"instructions" => {
|
||||
return (
|
||||
Some(Transition::Push(OptimizeCommute::cutscene(ctx, app))),
|
||||
Some(Transition::Push((Challenge::find(
|
||||
&GameplayMode::OptimizeCommute(self.person, self.goal),
|
||||
)
|
||||
.0
|
||||
.cutscene
|
||||
.unwrap())(
|
||||
ctx, app
|
||||
))),
|
||||
false,
|
||||
);
|
||||
}
|
||||
@ -192,11 +247,12 @@ fn make_top_center(
|
||||
after: Duration,
|
||||
done: usize,
|
||||
trips: usize,
|
||||
goal: Duration,
|
||||
) -> Composite {
|
||||
let mut txt = Text::from(Line(format!("Total trip time: {} (", after)));
|
||||
txt.append_all(cmp_duration_shorter(after, before));
|
||||
txt.append(Line(")"));
|
||||
let sentiment = if before - after >= GOAL {
|
||||
let sentiment = if before - after >= goal {
|
||||
"../data/system/assets/tools/happy.svg"
|
||||
} else {
|
||||
"../data/system/assets/tools/sad.svg"
|
||||
@ -213,7 +269,7 @@ fn make_top_center(
|
||||
.draw_text(ctx)
|
||||
.margin_right(20),
|
||||
txt.draw(ctx).margin_right(20),
|
||||
format!("Goal: {} faster", GOAL)
|
||||
format!("Goal: {} faster", goal)
|
||||
.draw_text(ctx)
|
||||
.margin_right(5),
|
||||
Widget::draw_svg(ctx, sentiment).centered_vert(),
|
||||
@ -232,7 +288,10 @@ fn final_score(
|
||||
mode: GameplayMode,
|
||||
before: Duration,
|
||||
after: Duration,
|
||||
goal: Duration,
|
||||
) -> Transition {
|
||||
let mut next_mode: Option<GameplayMode> = None;
|
||||
|
||||
let msg = if before == after {
|
||||
format!(
|
||||
"The VIP's commute still takes a total of {}. Were you asleep on the job? Try \
|
||||
@ -246,14 +305,14 @@ fn final_score(
|
||||
me over?!",
|
||||
before, after
|
||||
)
|
||||
} else if before - after < GOAL {
|
||||
} else if before - after < goal {
|
||||
format!(
|
||||
"The VIP's commute went from {} total to {}. Hmm... that's {} faster. But didn't I \
|
||||
tell you to speed things up by {} at least?",
|
||||
before,
|
||||
after,
|
||||
before - after,
|
||||
GOAL
|
||||
goal
|
||||
)
|
||||
} else {
|
||||
// Blindly record the high school
|
||||
@ -266,13 +325,15 @@ fn final_score(
|
||||
.entry(mode.clone())
|
||||
.or_insert_with(Vec::new);
|
||||
scores.push(HighScore {
|
||||
goal: GOAL,
|
||||
goal,
|
||||
score: before - after,
|
||||
edits_name: app.primary.map.get_edits().edits_name.clone(),
|
||||
});
|
||||
scores.sort_by_key(|s| s.score);
|
||||
scores.reverse();
|
||||
|
||||
next_mode = Challenge::find(&mode).1.map(|c| c.gameplay);
|
||||
|
||||
format!(
|
||||
"Alright, you somehow managed to shave {} down from the VIP's original commute of {}. \
|
||||
I guess that'll do. Maybe you're not totally useless after all.",
|
||||
@ -292,8 +353,13 @@ fn final_score(
|
||||
.padding(10),
|
||||
Widget::col(vec![
|
||||
msg.draw_text(ctx),
|
||||
// TODO Adjust wording, optional continue option
|
||||
// TODO Adjust wording
|
||||
Btn::text_bg2("Try again").build_def(ctx, None),
|
||||
if next_mode.is_some() {
|
||||
Btn::text_bg2("Next challenge").build_def(ctx, None)
|
||||
} else {
|
||||
Widget::nothing()
|
||||
},
|
||||
Btn::text_bg2("Back to challenges").build_def(ctx, None),
|
||||
])
|
||||
.outline(10.0, Color::BLACK)
|
||||
@ -303,6 +369,7 @@ fn final_score(
|
||||
)
|
||||
.build(ctx),
|
||||
retry: mode,
|
||||
next_mode,
|
||||
}))
|
||||
}
|
||||
|
||||
@ -333,6 +400,7 @@ impl ContextualActions for Actions {
|
||||
struct FinalScore {
|
||||
composite: Composite,
|
||||
retry: GameplayMode,
|
||||
next_mode: Option<GameplayMode>,
|
||||
}
|
||||
|
||||
impl State for FinalScore {
|
||||
@ -342,6 +410,14 @@ impl State for FinalScore {
|
||||
"Try again" => {
|
||||
Transition::Replace(Box::new(SandboxMode::new(ctx, app, self.retry.clone())))
|
||||
}
|
||||
"Next challenge" => Transition::Clear(vec![
|
||||
main_menu(ctx, app),
|
||||
Box::new(SandboxMode::new(ctx, app, self.next_mode.clone().unwrap())),
|
||||
(Challenge::find(self.next_mode.as_ref().unwrap())
|
||||
.0
|
||||
.cutscene
|
||||
.unwrap())(ctx, app),
|
||||
]),
|
||||
"Back to challenges" => {
|
||||
Transition::Clear(vec![main_menu(ctx, app), challenges_picker(ctx, app)])
|
||||
}
|
||||
|
@ -9,8 +9,7 @@ mod tutorial;
|
||||
|
||||
pub use self::tutorial::{Tutorial, TutorialPointer, TutorialState};
|
||||
use crate::app::App;
|
||||
use crate::challenges;
|
||||
use crate::challenges::challenges_picker;
|
||||
use crate::challenges::{challenges_picker, Challenge};
|
||||
use crate::common::{CommonState, ContextualActions};
|
||||
use crate::edit::EditMode;
|
||||
use crate::game::{msg, State, Transition};
|
||||
@ -28,7 +27,7 @@ use map_model::{EditCmd, EditIntersection, Map, MapEdits};
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use sim::{Analytics, PersonID, Scenario, ScenarioGenerator};
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone)]
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||
pub enum GameplayMode {
|
||||
// TODO Maybe this should be "sandbox"
|
||||
// Map path
|
||||
@ -40,7 +39,7 @@ pub enum GameplayMode {
|
||||
FixTrafficSignals,
|
||||
// TODO Kinda gross. What stage in the tutorial?
|
||||
FixTrafficSignalsTutorial(usize),
|
||||
OptimizeCommute(PersonID),
|
||||
OptimizeCommute(PersonID, Duration),
|
||||
|
||||
// current
|
||||
Tutorial(TutorialPointer),
|
||||
@ -93,7 +92,7 @@ impl GameplayMode {
|
||||
GameplayMode::FixTrafficSignalsTutorial(_) => {
|
||||
abstutil::path_synthetic_map("signal_single")
|
||||
}
|
||||
GameplayMode::OptimizeCommute(_) => abstutil::path_map("montlake"),
|
||||
GameplayMode::OptimizeCommute(_, _) => abstutil::path_map("montlake"),
|
||||
GameplayMode::Tutorial(_) => abstutil::path_map("montlake"),
|
||||
}
|
||||
}
|
||||
@ -252,7 +251,9 @@ impl GameplayMode {
|
||||
GameplayMode::FixTrafficSignals | GameplayMode::FixTrafficSignalsTutorial(_) => {
|
||||
fix_traffic_signals::FixTrafficSignals::new(ctx, app, self.clone())
|
||||
}
|
||||
GameplayMode::OptimizeCommute(p) => commute::OptimizeCommute::new(ctx, app, *p),
|
||||
GameplayMode::OptimizeCommute(p, goal) => {
|
||||
commute::OptimizeCommute::new(ctx, app, *p, *goal)
|
||||
}
|
||||
GameplayMode::Tutorial(current) => Tutorial::new(ctx, app, *current),
|
||||
}
|
||||
}
|
||||
@ -317,16 +318,7 @@ fn challenge_controller(
|
||||
title: &str,
|
||||
extra_rows: Vec<Widget>,
|
||||
) -> WrappedComposite {
|
||||
// Scrape the description
|
||||
let mut description = Vec::new();
|
||||
'OUTER: for (_, stages) in challenges::all_challenges(true) {
|
||||
for challenge in stages {
|
||||
if challenge.gameplay == gameplay {
|
||||
description = challenge.description.clone();
|
||||
break 'OUTER;
|
||||
}
|
||||
}
|
||||
}
|
||||
let description = Challenge::find(&gameplay).0.description;
|
||||
|
||||
let mut rows = vec![challenge_header(ctx, title)];
|
||||
rows.extend(extra_rows);
|
||||
|
@ -31,7 +31,7 @@ pub struct Tutorial {
|
||||
warped: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct TutorialPointer {
|
||||
pub stage: usize,
|
||||
// Index into messages. messages.len() means the actual task.
|
||||
|
Loading…
Reference in New Issue
Block a user