dont force reset from midnight in survivor mode!

This commit is contained in:
Dustin Carlino 2020-06-11 16:30:31 -07:00
parent 4e746cc033
commit 269bdc9171
6 changed files with 101 additions and 34 deletions

View File

@ -134,7 +134,12 @@ impl Btn {
pub fn plaintext<I: Into<String>>(label: I) -> BtnBuilder { pub fn plaintext<I: Into<String>>(label: I) -> BtnBuilder {
let label = label.into(); let label = label.into();
BtnBuilder::PlainText(label.clone(), Text::from(Line(label)), None) BtnBuilder::PlainText {
label: label.clone(),
txt: Text::from(Line(label)),
maybe_tooltip: None,
pad: (15.0, 8.0),
}
} }
pub fn text_fg<I: Into<String>>(label: I) -> BtnBuilder { pub fn text_fg<I: Into<String>>(label: I) -> BtnBuilder {
@ -205,7 +210,12 @@ pub enum BtnBuilder {
pad: usize, pad: usize,
}, },
TextFG(String, Text, Option<Text>), TextFG(String, Text, Option<Text>),
PlainText(String, Text, Option<Text>), PlainText {
label: String,
txt: Text,
maybe_tooltip: Option<Text>,
pad: (f64, f64),
},
TextBG { TextBG {
label: String, label: String,
maybe_tooltip: Option<Text>, maybe_tooltip: Option<Text>,
@ -220,11 +230,14 @@ pub enum BtnBuilder {
impl BtnBuilder { impl BtnBuilder {
pub fn tooltip(mut self, tooltip: Text) -> BtnBuilder { pub fn tooltip(mut self, tooltip: Text) -> BtnBuilder {
match self { match self {
BtnBuilder::TextFG(_, _, ref mut t) BtnBuilder::TextFG(_, _, ref mut maybe_tooltip)
| BtnBuilder::PlainText(_, _, ref mut t) | BtnBuilder::PlainText {
| BtnBuilder::Custom(_, _, _, ref mut t) => { ref mut maybe_tooltip,
assert!(t.is_none()); ..
*t = Some(tooltip); }
| BtnBuilder::Custom(_, _, _, ref mut maybe_tooltip) => {
assert!(maybe_tooltip.is_none());
*maybe_tooltip = Some(tooltip);
} }
BtnBuilder::SVG { BtnBuilder::SVG {
ref mut maybe_tooltip, ref mut maybe_tooltip,
@ -264,6 +277,10 @@ impl BtnBuilder {
*pad = new_pad; *pad = new_pad;
self self
} }
BtnBuilder::PlainText { ref mut pad, .. } => {
*pad = (new_pad as f64, new_pad as f64);
self
}
_ => unreachable!(), _ => unreachable!(),
} }
} }
@ -336,25 +353,24 @@ impl BtnBuilder {
.outline(2.0, Color::WHITE) .outline(2.0, Color::WHITE)
} }
// Same as TextFG without the outline // Same as TextFG without the outline
BtnBuilder::PlainText(_, normal_txt, maybe_t) => { BtnBuilder::PlainText {
// TODO Padding here is unfortunate, but I don't understand when the flexbox padding txt,
// actually works. maybe_tooltip,
let horiz_padding = 15.0; pad,
let vert_padding = 8.0; ..
} => {
let normal_txt = txt;
let unselected_batch = normal_txt.clone().render_ctx(ctx); let unselected_batch = normal_txt.clone().render_ctx(ctx);
let dims = unselected_batch.get_dims(); let dims = unselected_batch.get_dims();
let selected_batch = normal_txt.change_fg(Color::ORANGE).render_ctx(ctx); let selected_batch = normal_txt.change_fg(Color::ORANGE).render_ctx(ctx);
assert_eq!(dims, selected_batch.get_dims()); assert_eq!(dims, selected_batch.get_dims());
let geom = Polygon::rectangle( let geom = Polygon::rectangle(dims.width + 2.0 * pad.0, dims.height + 2.0 * pad.1);
dims.width + 2.0 * horiz_padding,
dims.height + 2.0 * vert_padding,
);
let mut normal = GeomBatch::new(); let mut normal = GeomBatch::new();
normal.append(unselected_batch.translate(horiz_padding, vert_padding)); normal.append(unselected_batch.translate(pad.0, pad.1));
let mut hovered = GeomBatch::new(); let mut hovered = GeomBatch::new();
hovered.append(selected_batch.translate(horiz_padding, vert_padding)); hovered.append(selected_batch.translate(pad.0, pad.1));
Button::new( Button::new(
ctx, ctx,
@ -362,7 +378,7 @@ impl BtnBuilder {
hovered, hovered,
key, key,
&action_tooltip.into(), &action_tooltip.into(),
maybe_t, maybe_tooltip,
geom, geom,
) )
} }
@ -418,7 +434,7 @@ impl BtnBuilder {
BtnBuilder::SVG { .. } => panic!("Can't use build_def on an SVG button"), BtnBuilder::SVG { .. } => panic!("Can't use build_def on an SVG button"),
BtnBuilder::Custom(_, _, _, _) => panic!("Can't use build_def on a custom button"), BtnBuilder::Custom(_, _, _, _) => panic!("Can't use build_def on a custom button"),
BtnBuilder::TextFG(ref label, _, _) BtnBuilder::TextFG(ref label, _, _)
| BtnBuilder::PlainText(ref label, _, _) | BtnBuilder::PlainText { ref label, .. }
| BtnBuilder::TextBG { ref label, .. } => { | BtnBuilder::TextBG { ref label, .. } => {
assert!(!label.is_empty()); assert!(!label.is_empty());
let copy = label.clone(); let copy = label.clone();

View File

@ -509,6 +509,8 @@ pub struct PerMap {
pub current_flags: Flags, pub current_flags: Flags,
pub last_warped_from: Option<(Pt2D, f64)>, pub last_warped_from: Option<(Pt2D, f64)>,
pub sim_cb: Option<Box<dyn SimCallback>>, pub sim_cb: Option<Box<dyn SimCallback>>,
// If we ever left edit mode and resumed without restarting from midnight, this is true.
pub dirty_from_edits: bool,
} }
impl PerMap { impl PerMap {
@ -530,11 +532,13 @@ impl PerMap {
current_flags: flags.clone(), current_flags: flags.clone(),
last_warped_from: None, last_warped_from: None,
sim_cb: None, sim_cb: None,
dirty_from_edits: false,
} }
} }
// Returns whatever was there // Returns whatever was there
pub fn clear_sim(&mut self) -> Sim { pub fn clear_sim(&mut self) -> Sim {
self.dirty_from_edits = false;
std::mem::replace( std::mem::replace(
&mut self.sim, &mut self.sim,
Sim::new( Sim::new(

View File

@ -33,6 +33,8 @@ use std::collections::BTreeSet;
pub struct EditMode { pub struct EditMode {
tool_panel: WrappedComposite, tool_panel: WrappedComposite,
composite: Composite, composite: Composite,
orig_edits: MapEdits,
orig_dirty: bool,
// Retained state from the SandboxMode that spawned us // Retained state from the SandboxMode that spawned us
mode: GameplayMode, mode: GameplayMode,
@ -44,12 +46,15 @@ pub struct EditMode {
impl EditMode { impl EditMode {
pub fn new(ctx: &mut EventCtx, app: &mut App, mode: GameplayMode) -> EditMode { pub fn new(ctx: &mut EventCtx, app: &mut App, mode: GameplayMode) -> EditMode {
let orig_dirty = app.primary.dirty_from_edits;
assert!(app.suspended_sim.is_none()); assert!(app.suspended_sim.is_none());
app.suspended_sim = Some(app.primary.clear_sim()); app.suspended_sim = Some(app.primary.clear_sim());
let edits = app.primary.map.get_edits(); let edits = app.primary.map.get_edits();
EditMode { EditMode {
tool_panel: tool_panel(ctx, app), tool_panel: tool_panel(ctx, app),
composite: make_topcenter(ctx, app, &mode), composite: make_topcenter(ctx, app, &mode),
orig_edits: edits.clone(),
orig_dirty,
mode, mode,
top_panel_key: (edits.edits_name.clone(), edits.commands.len()), top_panel_key: (edits.edits_name.clone(), edits.commands.len()),
once: true, once: true,
@ -57,10 +62,17 @@ impl EditMode {
} }
fn quit(&self, ctx: &mut EventCtx, app: &mut App) -> Transition { fn quit(&self, ctx: &mut EventCtx, app: &mut App) -> Transition {
let time = app.suspended_sim.take().unwrap().time(); let old_sim = app.suspended_sim.take().unwrap();
app.layer = None;
ctx.loading_screen("apply edits", |ctx, mut timer| { // If nothing changed, short-circuit
app.layer = None; if app.primary.map.get_edits() == &self.orig_edits {
app.primary.sim = old_sim;
app.primary.dirty_from_edits = self.orig_dirty;
return Transition::Pop;
}
ctx.loading_screen("apply edits", move |ctx, mut timer| {
app.primary app.primary
.map .map
.recalculate_pathfinding_after_edits(&mut timer); .recalculate_pathfinding_after_edits(&mut timer);
@ -71,10 +83,16 @@ impl EditMode {
app.primary.map.save_edits(); app.primary.map.save_edits();
} }
if app.opts.resume_after_edit { if app.opts.resume_after_edit {
Transition::PopThenReplaceThenPush( if self.mode.reset_after_edits() {
Box::new(SandboxMode::new(ctx, app, self.mode.clone())), Transition::PopThenReplaceThenPush(
TimeWarpScreen::new(ctx, app, time, false), Box::new(SandboxMode::new(ctx, app, self.mode.clone())),
) TimeWarpScreen::new(ctx, app, old_sim.time(), false),
)
} else {
app.primary.sim = old_sim;
app.primary.dirty_from_edits = true;
Transition::Pop
}
} else { } else {
Transition::PopThenReplace(Box::new(SandboxMode::new(ctx, app, self.mode.clone()))) Transition::PopThenReplace(Box::new(SandboxMode::new(ctx, app, self.mode.clone())))
} }

View File

@ -22,7 +22,6 @@ pub struct FixTrafficSignals {
top_center: Composite, top_center: Composite,
meter: Composite, meter: Composite,
time: Time, time: Time,
once: bool,
done: bool, done: bool,
mode: GameplayMode, mode: GameplayMode,
} }
@ -55,7 +54,6 @@ impl FixTrafficSignals {
.build(ctx), .build(ctx),
meter: make_meter(ctx, app, None), meter: make_meter(ctx, app, None),
time: Time::START_OF_DAY, time: Time::START_OF_DAY,
once: true,
done: false, done: false,
mode: GameplayMode::FixTrafficSignals, mode: GameplayMode::FixTrafficSignals,
}) })
@ -109,9 +107,9 @@ impl GameplayState for FixTrafficSignals {
METER_HACK, METER_HACK,
); );
if self.once { // Normally we just do this once at the beginning, but because there are other paths to
self.once = false; // reseting (like jump-to-time), it's safest just to do this.
assert!(app.primary.sim_cb.is_none()); if app.primary.sim_cb.is_none() {
app.primary.sim_cb = Some(Box::new(FindDelayedIntersections { app.primary.sim_cb = Some(Box::new(FindDelayedIntersections {
halt_limit: THRESHOLD, halt_limit: THRESHOLD,
report_limit: Duration::minutes(1), report_limit: Duration::minutes(1),
@ -252,6 +250,22 @@ impl GameplayState for FixTrafficSignals {
&mut app.primary, &mut app.primary,
))); )));
} }
"explain score" => {
// TODO Adjust wording
return Some(Transition::Push(FYI::new(
ctx,
Text::from_multiline(vec![
Line("You changed some traffic signals in the middle of the day."),
Line(
"First see if you can survive for a full day, making changes \
along the way.",
),
Line("Then you should check if your changes work from midnight."),
])
.draw(ctx),
app.cs.panel_bg,
)));
}
_ => unreachable!(), _ => unreachable!(),
}, },
None => {} None => {}
@ -308,6 +322,14 @@ fn make_meter(
]) ])
} else { } else {
Widget::row(vec![ Widget::row(vec![
if app.primary.dirty_from_edits {
Btn::plaintext("(!)")
.pad(0)
.build(ctx, "explain score", None)
.margin_right(10)
} else {
Widget::nothing()
},
Text::from_all(vec![Line("Worst delay: "), Line("none!").secondary()]) Text::from_all(vec![Line("Worst delay: "), Line("none!").secondary()])
.draw(ctx), .draw(ctx),
Widget::draw_svg_transform( Widget::draw_svg_transform(

View File

@ -145,6 +145,13 @@ impl GameplayMode {
} }
} }
pub fn reset_after_edits(&self) -> bool {
match self {
GameplayMode::FixTrafficSignals => false,
_ => true,
}
}
pub fn allows(&self, edits: &MapEdits) -> bool { pub fn allows(&self, edits: &MapEdits) -> bool {
for cmd in &edits.commands { for cmd in &edits.commands {
match cmd { match cmd {

View File

@ -7,7 +7,7 @@ use geom::Speed;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet}; use std::collections::{BTreeMap, BTreeSet};
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub struct MapEdits { pub struct MapEdits {
pub edits_name: String, pub edits_name: String,
pub commands: Vec<EditCmd>, pub commands: Vec<EditCmd>,
@ -29,7 +29,7 @@ pub enum EditIntersection {
Closed, Closed,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub enum EditCmd { pub enum EditCmd {
ChangeLaneType { ChangeLaneType {
id: LaneID, id: LaneID,