convert the last caller away from wizard...

This commit is contained in:
Dustin Carlino 2020-08-07 13:52:27 -07:00
parent f61881f6fa
commit b881fa605b
14 changed files with 199 additions and 89 deletions

View File

@ -1,6 +1,6 @@
use crate::app::{App, PerMap}; use crate::app::{App, PerMap};
use crate::common::Tab; use crate::common::Tab;
use crate::game::{msg, State, Transition}; use crate::game::{PopupMsg, State, Transition};
use crate::helpers::ID; use crate::helpers::ID;
use crate::info::OpenTrip; use crate::info::OpenTrip;
use crate::sandbox::SandboxMode; use crate::sandbox::SandboxMode;
@ -133,7 +133,8 @@ impl State for DebugWarp {
if let Some(t) = warp_to_id(ctx, app, &input) { if let Some(t) = warp_to_id(ctx, app, &input) {
t t
} else { } else {
Transition::Replace(msg( Transition::Replace(PopupMsg::new(
ctx,
"Bad warp ID", "Bad warp ID",
vec![format!("{} isn't a valid ID", input)], vec![format!("{} isn't a valid ID", input)],
)) ))

View File

@ -5,7 +5,7 @@ pub mod shared_row;
use crate::app::{App, ShowLayers, ShowObject}; use crate::app::{App, ShowLayers, ShowObject};
use crate::common::{tool_panel, CommonState, ContextualActions}; use crate::common::{tool_panel, CommonState, ContextualActions};
use crate::game::{msg, ChooseSomething, DrawBaselayer, PromptInput, State, Transition}; use crate::game::{ChooseSomething, DrawBaselayer, PopupMsg, PromptInput, State, Transition};
use crate::helpers::ID; use crate::helpers::ID;
use crate::options::OptionsPanel; use crate::options::OptionsPanel;
use crate::render::{calculate_corners, DrawOptions}; use crate::render::{calculate_corners, DrawOptions};
@ -145,7 +145,8 @@ impl State for DebugMode {
app.recalculate_current_selection(ctx); app.recalculate_current_selection(ctx);
None None
} }
None => Some(Transition::Push(msg( None => Some(Transition::Push(PopupMsg::new(
ctx,
"Error", "Error",
vec![format!( vec![format!(
"Couldn't load previous savestate {:?}", "Couldn't load previous savestate {:?}",
@ -170,7 +171,8 @@ impl State for DebugMode {
app.recalculate_current_selection(ctx); app.recalculate_current_selection(ctx);
None None
} }
None => Some(Transition::Push(msg( None => Some(Transition::Push(PopupMsg::new(
ctx,
"Error", "Error",
vec![format!("Couldn't load next savestate {:?}", next_state)], vec![format!("Couldn't load next savestate {:?}", next_state)],
))), ))),
@ -507,7 +509,8 @@ impl ContextualActions for Actions {
app.primary.current_selection = None; app.primary.current_selection = None;
Transition::Keep Transition::Keep
} }
(ID::Car(c), "find front of blockage") => Transition::Push(msg( (ID::Car(c), "find front of blockage") => Transition::Push(PopupMsg::new(
ctx,
"Blockage results", "Blockage results",
vec![format!( vec![format!(
"{} is ultimately blocked by {}", "{} is ultimately blocked by {}",

View File

@ -1,6 +1,6 @@
use crate::app::{App, ShowEverything}; use crate::app::{App, ShowEverything};
use crate::common::{CityPicker, ColorLegend}; use crate::common::{CityPicker, ColorLegend};
use crate::game::{msg, State, Transition}; use crate::game::{PopupMsg, State, Transition};
use crate::helpers::{nice_map_name, open_browser, ID}; use crate::helpers::{nice_map_name, open_browser, ID};
use abstutil::{prettyprint_usize, Tags, Timer}; use abstutil::{prettyprint_usize, Tags, Timer};
use ezgui::{ use ezgui::{
@ -324,7 +324,8 @@ impl State for ParkingMapper {
} }
"Generate OsmChange file" => { "Generate OsmChange file" => {
if self.data.is_empty() { if self.data.is_empty() {
return Transition::Push(msg( return Transition::Push(PopupMsg::new(
ctx,
"No changes yet", "No changes yet",
vec!["Map some parking first"], vec!["Map some parking first"],
)); ));
@ -337,11 +338,14 @@ impl State for ParkingMapper {
timer, timer,
) )
}) { }) {
Ok(()) => Transition::Push(msg( Ok(()) => Transition::Push(PopupMsg::new(
ctx,
"Diff generated", "Diff generated",
vec!["diff.osc created. Load it in JOSM, verify, and upload!"], vec!["diff.osc created. Load it in JOSM, verify, and upload!"],
)), )),
Err(err) => Transition::Push(msg("Error", vec![format!("{}", err)])), Err(err) => {
Transition::Push(PopupMsg::new(ctx, "Error", vec![format!("{}", err)]))
}
}; };
} }
"change map" => { "change map" => {
@ -469,7 +473,8 @@ impl State for ChangeWay {
.current_choice() .current_choice()
.clone(); .clone();
if value == Value::Complicated { if value == Value::Complicated {
Transition::Replace(msg( Transition::Replace(PopupMsg::new(
ctx,
"Complicated road", "Complicated road",
vec![ vec![
"You'll have to manually split the way in ID or JOSM and apply \ "You'll have to manually split the way in ID or JOSM and apply \

View File

@ -1,7 +1,7 @@
use crate::app::App; use crate::app::App;
use crate::edit::select::RoadSelector; use crate::edit::select::RoadSelector;
use crate::edit::{apply_map_edits, change_speed_limit, try_change_lt}; use crate::edit::{apply_map_edits, change_speed_limit, try_change_lt};
use crate::game::{msg, State, Transition}; use crate::game::{PopupMsg, State, Transition};
use ezgui::{ use ezgui::{
hotkey, Btn, Choice, Composite, Drawable, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, hotkey, Btn, Choice, Composite, Drawable, EventCtx, GfxCtx, HorizontalAlignment, Key, Line,
Outcome, TextExt, VerticalAlignment, Widget, Outcome, TextExt, VerticalAlignment, Widget,
@ -219,7 +219,7 @@ fn change_lane_types(
timer.next(); timer.next();
for l in app.primary.map.get_r(*r).all_lanes() { for l in app.primary.map.get_r(*r).all_lanes() {
if app.primary.map.get_l(l).lane_type == from { if app.primary.map.get_l(l).lane_type == from {
match try_change_lt(&mut app.primary.map, l, to) { match try_change_lt(ctx, &mut app.primary.map, l, to) {
Ok(cmd) => { Ok(cmd) => {
let mut edits = app.primary.map.get_edits().clone(); let mut edits = app.primary.map.get_edits().clone();
edits.commands.push(cmd); edits.commands.push(cmd);
@ -239,7 +239,8 @@ fn change_lane_types(
// TODO Need to express the errors in some form that we can union here. // TODO Need to express the errors in some form that we can union here.
msg( PopupMsg::new(
ctx,
"Changed lane types", "Changed lane types",
vec![format!( vec![format!(
"Changed {} {:?} lanes to {:?} lanes. {} errors", "Changed {} {:?} lanes to {:?} lanes. {} errors",

View File

@ -157,24 +157,26 @@ impl State for LaneEditor {
"Revert" => { "Revert" => {
// TODO It's hard to revert both changes at once. // TODO It's hard to revert both changes at once.
if let Some(lt) = map.get_edits().original_lts.get(&self.l).cloned() { if let Some(lt) = map.get_edits().original_lts.get(&self.l).cloned() {
try_change_lt(map, self.l, lt) try_change_lt(ctx, map, self.l, lt)
} else { } else {
try_reverse(map, self.l) try_reverse(ctx, map, self.l)
} }
} }
"reverse lane direction" => try_reverse(map, self.l), "reverse lane direction" => try_reverse(ctx, map, self.l),
"convert to a driving lane" => { "convert to a driving lane" => {
try_change_lt(map, self.l, LaneType::Driving) try_change_lt(ctx, map, self.l, LaneType::Driving)
} }
"convert to a protected bike lane" => { "convert to a protected bike lane" => {
try_change_lt(map, self.l, LaneType::Biking) try_change_lt(ctx, map, self.l, LaneType::Biking)
}
"convert to a bus-only lane" => {
try_change_lt(ctx, map, self.l, LaneType::Bus)
} }
"convert to a bus-only lane" => try_change_lt(map, self.l, LaneType::Bus),
"convert to an on-street parking lane" => { "convert to an on-street parking lane" => {
try_change_lt(map, self.l, LaneType::Parking) try_change_lt(ctx, map, self.l, LaneType::Parking)
} }
"close for construction" => { "close for construction" => {
try_change_lt(map, self.l, LaneType::Construction) try_change_lt(ctx, map, self.l, LaneType::Construction)
} }
_ => unreachable!(), _ => unreachable!(),
}; };

View File

@ -19,7 +19,7 @@ pub use self::validate::{
use crate::app::{App, ShowEverything}; use crate::app::{App, ShowEverything};
use crate::common::{tool_panel, CommonState, Warping}; use crate::common::{tool_panel, CommonState, Warping};
use crate::debug::DebugMode; use crate::debug::DebugMode;
use crate::game::{msg, State, Transition}; use crate::game::{PopupMsg, State, Transition};
use crate::helpers::ID; use crate::helpers::ID;
use crate::options::OptionsPanel; use crate::options::OptionsPanel;
use crate::render::{DrawIntersection, DrawMap, DrawRoad}; use crate::render::{DrawIntersection, DrawMap, DrawRoad};
@ -489,7 +489,11 @@ impl State for LoadEdits {
LoadEdits::new(ctx, app, self.mode.clone()), LoadEdits::new(ctx, app, self.mode.clone()),
// TODO Menu draws at a weird Z-order to deal with tooltips, so now the // TODO Menu draws at a weird Z-order to deal with tooltips, so now the
// menu underneath bleeds through // menu underneath bleeds through
msg("Error", vec![format!("Can't load {}", path), err.clone()]), PopupMsg::new(
ctx,
"Error",
vec![format!("Can't load {}", path), err.clone()],
),
), ),
} }
} }

View File

@ -1,7 +1,7 @@
use crate::app::{App, ShowEverything}; use crate::app::{App, ShowEverything};
use crate::common::CommonState; use crate::common::CommonState;
use crate::edit::{apply_map_edits, check_sidewalk_connectivity, StopSignEditor}; use crate::edit::{apply_map_edits, check_sidewalk_connectivity, StopSignEditor};
use crate::game::{msg, ChooseSomething, DrawBaselayer, State, Transition}; use crate::game::{ChooseSomething, DrawBaselayer, PopupMsg, State, Transition};
use crate::render::{ use crate::render::{
draw_signal_phase, make_signal_diagram, DrawOptions, DrawTurnGroup, BIG_ARROW_THICKNESS, draw_signal_phase, make_signal_diagram, DrawOptions, DrawTurnGroup, BIG_ARROW_THICKNESS,
}; };
@ -767,7 +767,8 @@ fn check_for_missing_groups(
app.primary.map.incremental_edit_traffic_signal(new_signal); app.primary.map.incremental_edit_traffic_signal(new_signal);
*composite = make_signal_diagram(ctx, app, id, 0, true); *composite = make_signal_diagram(ctx, app, id, 0, true);
Transition::Push(msg( Transition::Push(PopupMsg::new(
ctx,
"Error: missing turns", "Error: missing turns",
vec![ vec![
format!("{} turns are missing from this traffic signal", num_missing), format!("{} turns are missing from this traffic signal", num_missing),

View File

@ -1,6 +1,6 @@
use crate::app::App; use crate::app::App;
use crate::common::ColorDiscrete; use crate::common::ColorDiscrete;
use crate::game::{msg, State, WizardState}; use crate::game::{PopupMsg, State};
use abstutil::Timer; use abstutil::Timer;
use ezgui::{Color, EventCtx}; use ezgui::{Color, EventCtx};
use map_model::{connectivity, EditCmd, LaneID, LaneType, Map, PathConstraints}; use map_model::{connectivity, EditCmd, LaneID, LaneType, Map, PathConstraints};
@ -38,22 +38,23 @@ pub fn check_sidewalk_connectivity(
return None; return None;
} }
let mut err_state = msg(
"Error",
vec![format!(
"Can't close this intersection; {} sidewalks disconnected",
newly_disconnected.len()
)],
);
let mut c = ColorDiscrete::new(app, vec![("disconnected", Color::RED)]); let mut c = ColorDiscrete::new(app, vec![("disconnected", Color::RED)]);
let num = newly_disconnected.len();
for l in newly_disconnected { for l in newly_disconnected {
c.add_l(*l, "disconnected"); c.add_l(*l, "disconnected");
} }
let (unzoomed, zoomed, _) = c.build(ctx); let (unzoomed, zoomed, _) = c.build(ctx);
err_state.downcast_mut::<WizardState>().unwrap().also_draw = Some((unzoomed, zoomed));
Some(err_state) Some(PopupMsg::also_draw(
ctx,
"Error",
vec![format!(
"Can't close this intersection; {} sidewalks disconnected",
num
)],
unzoomed,
zoomed,
))
} }
#[allow(unused)] #[allow(unused)]
@ -96,25 +97,24 @@ pub fn check_blackholes(ctx: &mut EventCtx, app: &mut App, cmd: EditCmd) -> Opti
return None; return None;
} }
let mut err_state = msg(
"Error",
vec![format!(
"{} lanes have been disconnected",
newly_disconnected.len()
)],
);
let mut c = ColorDiscrete::new(app, vec![("disconnected", Color::RED)]); let mut c = ColorDiscrete::new(app, vec![("disconnected", Color::RED)]);
let num = newly_disconnected.len();
for l in newly_disconnected { for l in newly_disconnected {
c.add_l(l, "disconnected"); c.add_l(l, "disconnected");
} }
let (unzoomed, zoomed, _) = c.build(ctx); let (unzoomed, zoomed, _) = c.build(ctx);
err_state.downcast_mut::<WizardState>().unwrap().also_draw = Some((unzoomed, zoomed));
Some(err_state) Some(PopupMsg::also_draw(
ctx,
"Error",
vec![format!("{} lanes have been disconnected", num)],
unzoomed,
zoomed,
))
} }
pub fn try_change_lt( pub fn try_change_lt(
ctx: &mut EventCtx,
map: &mut Map, map: &mut Map,
l: LaneID, l: LaneID,
new_lt: LaneType, new_lt: LaneType,
@ -158,14 +158,15 @@ pub fn try_change_lt(
if errors.is_empty() { if errors.is_empty() {
Ok(cmd) Ok(cmd)
} else { } else {
Err(msg("Error", errors)) Err(PopupMsg::new(ctx, "Error", errors))
} }
} }
pub fn try_reverse(map: &Map, l: LaneID) -> Result<EditCmd, Box<dyn State>> { pub fn try_reverse(ctx: &mut EventCtx, map: &Map, l: LaneID) -> Result<EditCmd, Box<dyn State>> {
let lane = map.get_l(l); let lane = map.get_l(l);
if map.get_r(lane.parent).dir_and_offset(l).1 != 0 { if map.get_r(lane.parent).dir_and_offset(l).1 != 0 {
Err(msg( Err(PopupMsg::new(
ctx,
"Error", "Error",
vec!["You can only reverse the lanes next to the road's yellow center line"], vec!["You can only reverse the lanes next to the road's yellow center line"],
)) ))

View File

@ -4,8 +4,9 @@ use crate::pregame::TitleScreen;
use crate::render::DrawOptions; use crate::render::DrawOptions;
use crate::sandbox::{GameplayMode, SandboxMode}; use crate::sandbox::{GameplayMode, SandboxMode};
use ezgui::{ use ezgui::{
hotkey, Btn, Canvas, Choice, Composite, Drawable, EventCtx, GfxCtx, HorizontalAlignment, Key, hotkey, hotkeys, Btn, Canvas, Choice, Composite, Drawable, EventCtx, GeomBatch, GfxCtx,
Line, Menu, Outcome, ScreenRectangle, VerticalAlignment, Widget, Wizard, GUI, HorizontalAlignment, Key, Line, Menu, Outcome, ScreenRectangle, Text, VerticalAlignment,
Widget, Wizard, GUI,
}; };
use geom::Polygon; use geom::Polygon;
use map_model::PermanentMapEdits; use map_model::PermanentMapEdits;
@ -352,15 +353,6 @@ impl State for WizardState {
} }
} }
// TODO Word wrap
pub fn msg<S: Into<String>>(title: &'static str, lines: Vec<S>) -> Box<dyn State> {
let str_lines: Vec<String> = lines.into_iter().map(|l| l.into()).collect();
WizardState::new(Box::new(move |wiz, ctx, _| {
wiz.wrap(ctx).acknowledge(title, || str_lines.clone())?;
Some(Transition::Pop)
}))
}
pub struct ChooseSomething<T> { pub struct ChooseSomething<T> {
composite: Composite, composite: Composite,
cb: Box<dyn Fn(T, &mut EventCtx, &mut App) -> Transition>, cb: Box<dyn Fn(T, &mut EventCtx, &mut App) -> Transition>,
@ -497,3 +489,75 @@ impl State for PromptInput {
self.composite.draw(g); self.composite.draw(g);
} }
} }
pub struct PopupMsg {
composite: Composite,
unzoomed: Drawable,
zoomed: Drawable,
}
impl PopupMsg {
pub fn new<I: Into<String>>(ctx: &mut EventCtx, title: &str, lines: Vec<I>) -> Box<dyn State> {
PopupMsg::also_draw(
ctx,
title,
lines,
ctx.upload(GeomBatch::new()),
ctx.upload(GeomBatch::new()),
)
}
pub fn also_draw<I: Into<String>>(
ctx: &mut EventCtx,
title: &str,
lines: Vec<I>,
unzoomed: Drawable,
zoomed: Drawable,
) -> Box<dyn State> {
let mut txt = Text::new();
txt.add(Line(title).small_heading());
for l in lines {
txt.add(Line(l));
}
Box::new(PopupMsg {
composite: Composite::new(Widget::col(vec![
txt.draw(ctx),
Btn::text_bg2("OK").build_def(ctx, hotkeys(vec![Key::Enter, Key::Escape])),
]))
.build(ctx),
unzoomed,
zoomed,
})
}
}
impl State for PopupMsg {
fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
match self.composite.event(ctx) {
Outcome::Clicked(x) => match x.as_ref() {
"OK" => Transition::Pop,
_ => unreachable!(),
},
_ => {
if ctx.normal_left_click() && ctx.canvas.get_cursor_in_screen_space().is_none() {
return Transition::Pop;
}
Transition::Keep
}
}
}
fn draw_baselayer(&self) -> DrawBaselayer {
DrawBaselayer::PreviousState
}
fn draw(&self, g: &mut GfxCtx, app: &App) {
State::grey_out_map(g, app);
self.composite.draw(g);
if g.canvas.cam_zoom < app.opts.min_zoom_for_detail {
g.redraw(&self.unzoomed);
} else {
g.redraw(&self.zoomed);
}
}
}

View File

@ -2,7 +2,7 @@ use crate::app::App;
use crate::challenges::ChallengesPicker; use crate::challenges::ChallengesPicker;
use crate::devtools::DevToolsMode; use crate::devtools::DevToolsMode;
use crate::edit::apply_map_edits; use crate::edit::apply_map_edits;
use crate::game::{msg, DrawBaselayer, State, Transition}; use crate::game::{DrawBaselayer, PopupMsg, State, Transition};
use crate::helpers::open_browser; use crate::helpers::open_browser;
use crate::sandbox::gameplay::Tutorial; use crate::sandbox::gameplay::Tutorial;
use crate::sandbox::{GameplayMode, SandboxMode}; use crate::sandbox::{GameplayMode, SandboxMode};
@ -428,7 +428,11 @@ impl State for Proposals {
} }
}); });
if let Some(err) = maybe_err { if let Some(err) = maybe_err {
return Transition::Push(msg("Can't load proposal", vec![err])); return Transition::Push(PopupMsg::new(
ctx,
"Can't load proposal",
vec![err],
));
} else { } else {
app.layer = Some(Box::new(crate::layer::map::Static::edits(ctx, app))); app.layer = Some(Box::new(crate::layer::map::Static::edits(ctx, app)));
return Transition::Push(Box::new(SandboxMode::new( return Transition::Push(Box::new(SandboxMode::new(

View File

@ -1,7 +1,7 @@
use crate::app::App; use crate::app::App;
use crate::common::CityPicker; use crate::common::CityPicker;
use crate::edit::EditMode; use crate::edit::EditMode;
use crate::game::{msg, State, Transition}; use crate::game::{PopupMsg, State, Transition};
use crate::helpers::{checkbox_per_mode, nice_map_name}; use crate::helpers::{checkbox_per_mode, nice_map_name};
use crate::sandbox::gameplay::freeform::make_change_traffic; use crate::sandbox::gameplay::freeform::make_change_traffic;
use crate::sandbox::gameplay::{GameplayMode, GameplayState}; use crate::sandbox::gameplay::{GameplayMode, GameplayState};
@ -370,13 +370,15 @@ impl State for ChangeMode {
from_modes.remove(&to_mode); from_modes.remove(&to_mode);
if from_modes.is_empty() { if from_modes.is_empty() {
return Transition::Push(msg( return Transition::Push(PopupMsg::new(
ctx,
"Error", "Error",
vec!["You have to select at least one mode to convert from"], vec!["You have to select at least one mode to convert from"],
)); ));
} }
if p1 >= p2 { if p1 >= p2 {
return Transition::Push(msg( return Transition::Push(PopupMsg::new(
ctx,
"Error", "Error",
vec!["Your time range is backwards"], vec!["Your time range is backwards"],
)); ));

View File

@ -2,7 +2,7 @@ use crate::app::App;
use crate::common::{tool_panel, Minimap, Warping}; use crate::common::{tool_panel, Minimap, Warping};
use crate::cutscene::CutsceneBuilder; use crate::cutscene::CutsceneBuilder;
use crate::edit::EditMode; use crate::edit::EditMode;
use crate::game::{msg, State, Transition}; use crate::game::{PopupMsg, State, Transition};
use crate::helpers::ID; use crate::helpers::ID;
use crate::sandbox::gameplay::{GameplayMode, GameplayState}; use crate::sandbox::gameplay::{GameplayMode, GameplayState};
use crate::sandbox::{ use crate::sandbox::{
@ -265,7 +265,8 @@ impl Tutorial {
if !tut.score_delivered { if !tut.score_delivered {
tut.score_delivered = true; tut.score_delivered = true;
if before == after { if before == after {
return Some(Transition::Push(msg( return Some(Transition::Push(PopupMsg::new(
ctx,
"All trips completed", "All trips completed",
vec![ vec![
"Your changes didn't affect anything!", "Your changes didn't affect anything!",
@ -274,7 +275,8 @@ impl Tutorial {
))); )));
} }
if after > before { if after > before {
return Some(Transition::Push(msg( return Some(Transition::Push(PopupMsg::new(
ctx,
"All trips completed", "All trips completed",
vec![ vec![
"Your changes made things worse!".to_string(), "Your changes made things worse!".to_string(),
@ -288,7 +290,8 @@ impl Tutorial {
))); )));
} }
if before - after < CAR_BIKE_CONTENTION_GOAL { if before - after < CAR_BIKE_CONTENTION_GOAL {
return Some(Transition::Push(msg( return Some(Transition::Push(PopupMsg::new(
ctx,
"All trips completed", "All trips completed",
vec![ vec![
"Nice, you helped things a bit!".to_string(), "Nice, you helped things a bit!".to_string(),
@ -301,7 +304,8 @@ impl Tutorial {
], ],
))); )));
} }
return Some(Transition::Push(msg( return Some(Transition::Push(PopupMsg::new(
ctx,
"All trips completed", "All trips completed",
vec![format!( vec![format!(
"Awesome! All trips originally took {}, but now they only took {}", "Awesome! All trips originally took {}, but now they only took {}",
@ -1329,7 +1333,7 @@ pub fn actions(app: &App, id: ID) -> Vec<(Key, String)> {
} }
} }
pub fn execute(_: &mut EventCtx, app: &mut App, id: ID, action: String) -> Transition { pub fn execute(ctx: &mut EventCtx, app: &mut App, id: ID, action: String) -> Transition {
let mut tut = app.session.tutorial.as_mut().unwrap(); let mut tut = app.session.tutorial.as_mut().unwrap();
let response = match (id, action.as_ref()) { let response = match (id, action.as_ref()) {
(ID::Car(c), "draw WASH ME") => { (ID::Car(c), "draw WASH ME") => {
@ -1341,12 +1345,14 @@ pub fn execute(_: &mut EventCtx, app: &mut App, id: ID, action: String) -> Trans
if c == ESCORT { if c == ESCORT {
if is_parked { if is_parked {
tut.prank_done = true; tut.prank_done = true;
msg( PopupMsg::new(
ctx,
"Prank in progress", "Prank in progress",
vec!["You quickly scribble on the window..."], vec!["You quickly scribble on the window..."],
) )
} else { } else {
msg( PopupMsg::new(
ctx,
"Not yet!", "Not yet!",
vec![ vec![
"You're going to run up to an occupied car and draw on their windows?", "You're going to run up to an occupied car and draw on their windows?",
@ -1356,7 +1362,8 @@ pub fn execute(_: &mut EventCtx, app: &mut App, id: ID, action: String) -> Trans
) )
} }
} else if c.1 == VehicleType::Bike { } else if c.1 == VehicleType::Bike {
msg( PopupMsg::new(
ctx,
"That's a bike", "That's a bike",
vec![ vec![
"Achievement unlocked: You attempted to draw WASH ME on a cyclist.", "Achievement unlocked: You attempted to draw WASH ME on a cyclist.",
@ -1366,7 +1373,8 @@ pub fn execute(_: &mut EventCtx, app: &mut App, id: ID, action: String) -> Trans
], ],
) )
} else { } else {
msg( PopupMsg::new(
ctx,
"Wrong car", "Wrong car",
vec![ vec![
"You're looking at the wrong car.", "You're looking at the wrong car.",
@ -1382,7 +1390,8 @@ pub fn execute(_: &mut EventCtx, app: &mut App, id: ID, action: String) -> Trans
let percent = (app.primary.sim.get_free_onstreet_spots(l).len() as f64) let percent = (app.primary.sim.get_free_onstreet_spots(l).len() as f64)
/ (lane.number_parking_spots() as f64); / (lane.number_parking_spots() as f64);
if percent > 0.1 { if percent > 0.1 {
msg( PopupMsg::new(
ctx,
"Not quite", "Not quite",
vec![ vec![
format!("This lane has {:.0}% spots free", percent * 100.0), format!("This lane has {:.0}% spots free", percent * 100.0),
@ -1392,10 +1401,14 @@ pub fn execute(_: &mut EventCtx, app: &mut App, id: ID, action: String) -> Trans
) )
} else { } else {
tut.parking_found = true; tut.parking_found = true;
msg("Noice", vec!["Yup, parallel parking would be tough here!"]) PopupMsg::new(
ctx,
"Noice",
vec!["Yup, parallel parking would be tough here!"],
)
} }
} else { } else {
msg("Uhh..", vec!["That's not even a parking lane"]) PopupMsg::new(ctx, "Uhh..", vec!["That's not even a parking lane"])
} }
} }
_ => unreachable!(), _ => unreachable!(),

View File

@ -1,6 +1,6 @@
use crate::app::{App, FindDelayedIntersections, ShowEverything}; use crate::app::{App, FindDelayedIntersections, ShowEverything};
use crate::common::Warping; use crate::common::Warping;
use crate::game::{msg, DrawBaselayer, State, Transition}; use crate::game::{DrawBaselayer, PopupMsg, State, Transition};
use crate::helpers::ID; use crate::helpers::ID;
use crate::render::DrawOptions; use crate::render::DrawOptions;
use crate::sandbox::{GameplayMode, SandboxMode}; use crate::sandbox::{GameplayMode, SandboxMode};
@ -181,7 +181,8 @@ impl SpeedControls {
mode.clone(), mode.clone(),
)))); ))));
} else { } else {
return Some(Transition::Push(msg( return Some(Transition::Push(PopupMsg::new(
ctx,
"Error", "Error",
vec!["Sorry, you can't go rewind time from this mode."], vec!["Sorry, you can't go rewind time from this mode."],
))); )));
@ -284,7 +285,11 @@ impl SpeedControls {
// TODO Need to do this anywhere that steps the sim, like TimeWarpScreen. // TODO Need to do this anywhere that steps the sim, like TimeWarpScreen.
let alerts = app.primary.sim.clear_alerts(); let alerts = app.primary.sim.clear_alerts();
if !alerts.is_empty() { if !alerts.is_empty() {
let popup = msg("Alerts", alerts.iter().map(|(_, _, msg)| msg).collect()); let popup = PopupMsg::new(
ctx,
"Alerts",
alerts.iter().map(|(_, _, msg)| msg).collect(),
);
let maybe_id = match alerts[0].1 { let maybe_id = match alerts[0].1 {
AlertLocation::Nil => None, AlertLocation::Nil => None,
AlertLocation::Intersection(i) => Some(ID::Intersection(i)), AlertLocation::Intersection(i) => Some(ID::Intersection(i)),
@ -432,7 +437,8 @@ impl State for JumpToTime {
TimeWarpScreen::new(ctx, app, self.target, false), TimeWarpScreen::new(ctx, app, self.target, false),
); );
} else { } else {
return Transition::Replace(msg( return Transition::Replace(PopupMsg::new(
ctx,
"Error", "Error",
vec!["Sorry, you can't go rewind time from this mode."], vec!["Sorry, you can't go rewind time from this mode."],
)); ));
@ -541,7 +547,8 @@ impl State for TimeWarpScreen {
); );
for (t, maybe_i, alert) in app.primary.sim.clear_alerts() { for (t, maybe_i, alert) in app.primary.sim.clear_alerts() {
// TODO Just the first :( // TODO Just the first :(
return Transition::Replace(msg( return Transition::Replace(PopupMsg::new(
ctx,
"Alert", "Alert",
vec![format!("At {}, near {:?}, {}", t, maybe_i, alert)], vec![format!("At {}, near {:?}, {}", t, maybe_i, alert)],
)); ));

View File

@ -1,7 +1,7 @@
use crate::app::{App, ShowEverything}; use crate::app::{App, ShowEverything};
use crate::common::CommonState; use crate::common::CommonState;
use crate::edit::ClusterTrafficSignalEditor; use crate::edit::ClusterTrafficSignalEditor;
use crate::game::{msg, DrawBaselayer, State, Transition}; use crate::game::{DrawBaselayer, PopupMsg, State, Transition};
use crate::helpers::ID; use crate::helpers::ID;
use crate::render::{DrawOptions, BIG_ARROW_THICKNESS}; use crate::render::{DrawOptions, BIG_ARROW_THICKNESS};
use ezgui::{ use ezgui::{
@ -71,7 +71,8 @@ impl State for UberTurnPicker {
} }
"View uber-turns" => { "View uber-turns" => {
if self.members.len() < 2 { if self.members.len() < 2 {
return Transition::Push(msg( return Transition::Push(PopupMsg::new(
ctx,
"Error", "Error",
vec!["Select at least two intersections"], vec!["Select at least two intersections"],
)); ));
@ -86,7 +87,8 @@ impl State for UberTurnPicker {
} }
"Edit" => { "Edit" => {
if self.members.len() < 2 { if self.members.len() < 2 {
return Transition::Push(msg( return Transition::Push(PopupMsg::new(
ctx,
"Error", "Error",
vec!["Select at least two intersections"], vec!["Select at least two intersections"],
)); ));