Fix #367 by making all constructors of SandboxMode appropriately defer

work that must happen after the map/scenario are loaded.
This commit is contained in:
Dustin Carlino 2020-10-14 17:43:28 -05:00
parent e7fb9b1a4a
commit 1e0d8536a4
11 changed files with 36 additions and 20 deletions

View File

@ -257,7 +257,8 @@ impl State for ChallengesPicker {
}
"Start!" => {
let challenge = self.challenge.take().unwrap();
let sandbox = SandboxMode::new(ctx, app, challenge.gameplay.clone());
// Constructing the cutscene doesn't require the map/scenario to be loaded
let sandbox = SandboxMode::simple_new(ctx, app, challenge.gameplay.clone());
if let Some(cutscene) = challenge.cutscene {
Transition::Multi(vec![
Transition::Replace(sandbox),

View File

@ -70,7 +70,7 @@ impl Game {
} else {
let mode = maybe_mode
.unwrap_or_else(|| GameplayMode::Freeform(app.primary.map.get_name().clone()));
vec![SandboxMode::new(ctx, &mut app, mode)]
vec![SandboxMode::simple_new(ctx, &mut app, mode)]
};
if let Some(ss) = savestate {
// TODO This is weird, we're left in Freeform mode with the wrong UI. Can't instantiate

View File

@ -189,7 +189,7 @@ impl State for MainMenu {
} else {
"home_to_work"
};
return Transition::Push(SandboxMode::new(
return Transition::Push(SandboxMode::simple_new(
ctx,
app,
GameplayMode::PlayScenario(
@ -437,7 +437,7 @@ impl State for Proposals {
} else {
app.primary.layer =
Some(Box::new(crate::layer::map::Static::edits(ctx, app)));
Transition::Replace(SandboxMode::new(
Transition::Replace(SandboxMode::simple_new(
ctx,
app,
GameplayMode::PlayScenario(

View File

@ -218,7 +218,7 @@ impl GameplayState for FixTrafficSignals {
return Some(Transition::Push(FYI::new(ctx, contents, app.cs.panel_bg)));
}
"try again" => {
return Some(Transition::Replace(SandboxMode::new(
return Some(Transition::Replace(SandboxMode::simple_new(
ctx,
app,
self.mode.clone(),

View File

@ -50,7 +50,7 @@ impl GameplayState for Freeform {
Box::new(|ctx, app| {
Transition::Multi(vec![
Transition::Pop,
Transition::Replace(SandboxMode::new(
Transition::Replace(SandboxMode::simple_new(
ctx,
app,
GameplayMode::Freeform(app.primary.map.get_name().clone()),
@ -192,7 +192,7 @@ pub fn make_change_traffic(
Box::new(|scenario_name, ctx, app| {
Transition::Multi(vec![
Transition::Pop,
Transition::Replace(SandboxMode::new(
Transition::Replace(SandboxMode::simple_new(
ctx,
app,
if scenario_name == "none" {

View File

@ -270,7 +270,7 @@ impl State for FinalScore {
"Try again" => {
return Transition::Multi(vec![
Transition::Pop,
Transition::Replace(SandboxMode::new(ctx, app, self.retry.clone())),
Transition::Replace(SandboxMode::simple_new(ctx, app, self.retry.clone())),
]);
}
"Next challenge" => {
@ -320,7 +320,8 @@ impl State for FinalScore {
if self.chose_next {
return Transition::Clear(vec![
MainMenu::new(ctx, app),
SandboxMode::new(ctx, app, self.next_mode.clone().unwrap()),
// Constructing the cutscene doesn't require the map/scenario to be loaded.
SandboxMode::simple_new(ctx, app, self.next_mode.clone().unwrap()),
(Challenge::find(self.next_mode.as_ref().unwrap())
.0
.cutscene

View File

@ -73,7 +73,7 @@ impl GameplayState for PlayScenario {
};
Transition::Multi(vec![
Transition::Pop,
Transition::Replace(SandboxMode::new(ctx, app, mode)),
Transition::Replace(SandboxMode::simple_new(ctx, app, mode)),
])
}),
)))
@ -241,7 +241,7 @@ impl State for EditScenarioModifiers {
return Transition::Multi(vec![
Transition::Pop,
Transition::Replace(SandboxMode::new(
Transition::Replace(SandboxMode::simple_new(
ctx,
app,
GameplayMode::PlayScenario(

View File

@ -53,7 +53,8 @@ impl TutorialPointer {
impl Tutorial {
pub fn start(ctx: &mut EventCtx, app: &mut App) -> Transition {
Transition::Multi(vec![
Transition::Push(SandboxMode::new(
// Constructing the intro_story cutscene doesn't require the map/scenario to be loaded.
Transition::Push(SandboxMode::simple_new(
ctx,
app,
GameplayMode::Tutorial(
@ -662,7 +663,7 @@ fn make_bike_lane_scenario(map: &Map) -> ScenarioGenerator {
fn transition(ctx: &mut EventCtx, app: &mut App, tut: &mut TutorialState) -> Transition {
tut.reset_state();
let mode = GameplayMode::Tutorial(tut.current);
Transition::Replace(SandboxMode::new(ctx, app, mode))
Transition::Replace(SandboxMode::simple_new(ctx, app, mode))
}
impl TutorialState {

View File

@ -51,14 +51,17 @@ pub struct SandboxControls {
}
impl SandboxMode {
// TODO Audit all callers
pub fn new(ctx: &mut EventCtx, app: &mut App, mode: GameplayMode) -> Box<dyn State> {
/// If you don't need to chain any transitions after the SandboxMode that rely on its resources
/// being loaded, use this. Otherwise, see `async_new`.
pub fn simple_new(ctx: &mut EventCtx, app: &mut App, mode: GameplayMode) -> Box<dyn State> {
SandboxMode::async_new(ctx, app, mode, Box::new(|_, _| Vec::new()))
}
/// This does not immediately initialize anything (like loading the correct map, instantiating
/// the scenario, etc). That means if you're chaining this call with other transitions, you
/// probably need to defer running them using `finalize`.
// TODO Remove the unused ctx param? It affects lots of downstream callers; maybe better to
// leave it here in case this monstrosity is refactored again.
pub fn async_new(
_: &mut EventCtx,
app: &mut App,

View File

@ -165,7 +165,7 @@ impl SpeedControls {
}
"reset to midnight" => {
if let Some(mode) = maybe_mode {
return Some(Transition::Replace(SandboxMode::new(
return Some(Transition::Replace(SandboxMode::simple_new(
ctx,
app,
mode.clone(),

View File

@ -87,10 +87,20 @@ impl State for JumpToTime {
"jump to time" => {
if self.target < app.primary.sim.time() {
if let Some(mode) = self.maybe_mode.take() {
return Transition::Multi(vec![
Transition::Replace(SandboxMode::new(ctx, app, mode)),
Transition::Push(TimeWarpScreen::new(ctx, app, self.target, None)),
]);
let target_time = self.target;
return Transition::Replace(SandboxMode::async_new(
ctx,
app,
mode,
Box::new(move |ctx, app| {
vec![Transition::Push(TimeWarpScreen::new(
ctx,
app,
target_time,
None,
))]
}),
));
} else {
return Transition::Replace(PopupMsg::new(
ctx,