From 269bdc9171f1d2e11c82dd1de0835b0e68857781 Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Thu, 11 Jun 2020 16:30:31 -0700 Subject: [PATCH] dont force reset from midnight in survivor mode! --- ezgui/src/widgets/button.rs | 56 ++++++++++++------- game/src/app.rs | 4 ++ game/src/edit/mod.rs | 32 ++++++++--- .../sandbox/gameplay/fix_traffic_signals.rs | 32 +++++++++-- game/src/sandbox/gameplay/mod.rs | 7 +++ map_model/src/edits.rs | 4 +- 6 files changed, 101 insertions(+), 34 deletions(-) diff --git a/ezgui/src/widgets/button.rs b/ezgui/src/widgets/button.rs index 18bd2cd1f8..051923b6b2 100644 --- a/ezgui/src/widgets/button.rs +++ b/ezgui/src/widgets/button.rs @@ -134,7 +134,12 @@ impl Btn { pub fn plaintext>(label: I) -> BtnBuilder { 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>(label: I) -> BtnBuilder { @@ -205,7 +210,12 @@ pub enum BtnBuilder { pad: usize, }, TextFG(String, Text, Option), - PlainText(String, Text, Option), + PlainText { + label: String, + txt: Text, + maybe_tooltip: Option, + pad: (f64, f64), + }, TextBG { label: String, maybe_tooltip: Option, @@ -220,11 +230,14 @@ pub enum BtnBuilder { impl BtnBuilder { pub fn tooltip(mut self, tooltip: Text) -> BtnBuilder { match self { - BtnBuilder::TextFG(_, _, ref mut t) - | BtnBuilder::PlainText(_, _, ref mut t) - | BtnBuilder::Custom(_, _, _, ref mut t) => { - assert!(t.is_none()); - *t = Some(tooltip); + BtnBuilder::TextFG(_, _, ref mut maybe_tooltip) + | BtnBuilder::PlainText { + ref mut maybe_tooltip, + .. + } + | BtnBuilder::Custom(_, _, _, ref mut maybe_tooltip) => { + assert!(maybe_tooltip.is_none()); + *maybe_tooltip = Some(tooltip); } BtnBuilder::SVG { ref mut maybe_tooltip, @@ -264,6 +277,10 @@ impl BtnBuilder { *pad = new_pad; self } + BtnBuilder::PlainText { ref mut pad, .. } => { + *pad = (new_pad as f64, new_pad as f64); + self + } _ => unreachable!(), } } @@ -336,25 +353,24 @@ impl BtnBuilder { .outline(2.0, Color::WHITE) } // Same as TextFG without the outline - BtnBuilder::PlainText(_, normal_txt, maybe_t) => { - // TODO Padding here is unfortunate, but I don't understand when the flexbox padding - // actually works. - let horiz_padding = 15.0; - let vert_padding = 8.0; + BtnBuilder::PlainText { + txt, + maybe_tooltip, + pad, + .. + } => { + let normal_txt = txt; let unselected_batch = normal_txt.clone().render_ctx(ctx); let dims = unselected_batch.get_dims(); let selected_batch = normal_txt.change_fg(Color::ORANGE).render_ctx(ctx); assert_eq!(dims, selected_batch.get_dims()); - let geom = Polygon::rectangle( - dims.width + 2.0 * horiz_padding, - dims.height + 2.0 * vert_padding, - ); + let geom = Polygon::rectangle(dims.width + 2.0 * pad.0, dims.height + 2.0 * pad.1); 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(); - hovered.append(selected_batch.translate(horiz_padding, vert_padding)); + hovered.append(selected_batch.translate(pad.0, pad.1)); Button::new( ctx, @@ -362,7 +378,7 @@ impl BtnBuilder { hovered, key, &action_tooltip.into(), - maybe_t, + maybe_tooltip, geom, ) } @@ -418,7 +434,7 @@ impl BtnBuilder { 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::TextFG(ref label, _, _) - | BtnBuilder::PlainText(ref label, _, _) + | BtnBuilder::PlainText { ref label, .. } | BtnBuilder::TextBG { ref label, .. } => { assert!(!label.is_empty()); let copy = label.clone(); diff --git a/game/src/app.rs b/game/src/app.rs index 6a5a8cceb6..f5697ef481 100644 --- a/game/src/app.rs +++ b/game/src/app.rs @@ -509,6 +509,8 @@ pub struct PerMap { pub current_flags: Flags, pub last_warped_from: Option<(Pt2D, f64)>, pub sim_cb: Option>, + // If we ever left edit mode and resumed without restarting from midnight, this is true. + pub dirty_from_edits: bool, } impl PerMap { @@ -530,11 +532,13 @@ impl PerMap { current_flags: flags.clone(), last_warped_from: None, sim_cb: None, + dirty_from_edits: false, } } // Returns whatever was there pub fn clear_sim(&mut self) -> Sim { + self.dirty_from_edits = false; std::mem::replace( &mut self.sim, Sim::new( diff --git a/game/src/edit/mod.rs b/game/src/edit/mod.rs index 3fd1d959b6..59689b5dfa 100644 --- a/game/src/edit/mod.rs +++ b/game/src/edit/mod.rs @@ -33,6 +33,8 @@ use std::collections::BTreeSet; pub struct EditMode { tool_panel: WrappedComposite, composite: Composite, + orig_edits: MapEdits, + orig_dirty: bool, // Retained state from the SandboxMode that spawned us mode: GameplayMode, @@ -44,12 +46,15 @@ pub struct EditMode { impl 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()); app.suspended_sim = Some(app.primary.clear_sim()); let edits = app.primary.map.get_edits(); EditMode { tool_panel: tool_panel(ctx, app), composite: make_topcenter(ctx, app, &mode), + orig_edits: edits.clone(), + orig_dirty, mode, top_panel_key: (edits.edits_name.clone(), edits.commands.len()), once: true, @@ -57,10 +62,17 @@ impl EditMode { } 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| { - app.layer = None; + // If nothing changed, short-circuit + 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 .map .recalculate_pathfinding_after_edits(&mut timer); @@ -71,10 +83,16 @@ impl EditMode { app.primary.map.save_edits(); } if app.opts.resume_after_edit { - Transition::PopThenReplaceThenPush( - Box::new(SandboxMode::new(ctx, app, self.mode.clone())), - TimeWarpScreen::new(ctx, app, time, false), - ) + if self.mode.reset_after_edits() { + Transition::PopThenReplaceThenPush( + 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 { Transition::PopThenReplace(Box::new(SandboxMode::new(ctx, app, self.mode.clone()))) } diff --git a/game/src/sandbox/gameplay/fix_traffic_signals.rs b/game/src/sandbox/gameplay/fix_traffic_signals.rs index 1169ef1959..a942ab3858 100644 --- a/game/src/sandbox/gameplay/fix_traffic_signals.rs +++ b/game/src/sandbox/gameplay/fix_traffic_signals.rs @@ -22,7 +22,6 @@ pub struct FixTrafficSignals { top_center: Composite, meter: Composite, time: Time, - once: bool, done: bool, mode: GameplayMode, } @@ -55,7 +54,6 @@ impl FixTrafficSignals { .build(ctx), meter: make_meter(ctx, app, None), time: Time::START_OF_DAY, - once: true, done: false, mode: GameplayMode::FixTrafficSignals, }) @@ -109,9 +107,9 @@ impl GameplayState for FixTrafficSignals { METER_HACK, ); - if self.once { - self.once = false; - assert!(app.primary.sim_cb.is_none()); + // Normally we just do this once at the beginning, but because there are other paths to + // reseting (like jump-to-time), it's safest just to do this. + if app.primary.sim_cb.is_none() { app.primary.sim_cb = Some(Box::new(FindDelayedIntersections { halt_limit: THRESHOLD, report_limit: Duration::minutes(1), @@ -252,6 +250,22 @@ impl GameplayState for FixTrafficSignals { &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!(), }, None => {} @@ -308,6 +322,14 @@ fn make_meter( ]) } else { 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()]) .draw(ctx), Widget::draw_svg_transform( diff --git a/game/src/sandbox/gameplay/mod.rs b/game/src/sandbox/gameplay/mod.rs index cf757fa5b4..48a9705679 100644 --- a/game/src/sandbox/gameplay/mod.rs +++ b/game/src/sandbox/gameplay/mod.rs @@ -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 { for cmd in &edits.commands { match cmd { diff --git a/map_model/src/edits.rs b/map_model/src/edits.rs index 9fec7a0a35..cbd0dd6501 100644 --- a/map_model/src/edits.rs +++ b/map_model/src/edits.rs @@ -7,7 +7,7 @@ use geom::Speed; use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, BTreeSet}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct MapEdits { pub edits_name: String, pub commands: Vec, @@ -29,7 +29,7 @@ pub enum EditIntersection { Closed, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum EditCmd { ChangeLaneType { id: LaneID,