mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-01 02:33:54 +03:00
dont force reset from midnight in survivor mode!
This commit is contained in:
parent
4e746cc033
commit
269bdc9171
@ -134,7 +134,12 @@ impl Btn {
|
||||
|
||||
pub fn plaintext<I: Into<String>>(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<I: Into<String>>(label: I) -> BtnBuilder {
|
||||
@ -205,7 +210,12 @@ pub enum BtnBuilder {
|
||||
pad: usize,
|
||||
},
|
||||
TextFG(String, Text, Option<Text>),
|
||||
PlainText(String, Text, Option<Text>),
|
||||
PlainText {
|
||||
label: String,
|
||||
txt: Text,
|
||||
maybe_tooltip: Option<Text>,
|
||||
pad: (f64, f64),
|
||||
},
|
||||
TextBG {
|
||||
label: String,
|
||||
maybe_tooltip: Option<Text>,
|
||||
@ -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();
|
||||
|
@ -509,6 +509,8 @@ pub struct PerMap {
|
||||
pub current_flags: Flags,
|
||||
pub last_warped_from: Option<(Pt2D, f64)>,
|
||||
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 {
|
||||
@ -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(
|
||||
|
@ -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())))
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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 {
|
||||
|
@ -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<EditCmd>,
|
||||
@ -29,7 +29,7 @@ pub enum EditIntersection {
|
||||
Closed,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum EditCmd {
|
||||
ChangeLaneType {
|
||||
id: LaneID,
|
||||
|
Loading…
Reference in New Issue
Block a user