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 {
|
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();
|
||||||
|
@ -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(
|
||||||
|
@ -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())))
|
||||||
}
|
}
|
||||||
|
@ -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(
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user