diff --git a/game/src/edit/traffic_signals.rs b/game/src/edit/traffic_signals.rs index 7891a8938f..9436055d0b 100644 --- a/game/src/edit/traffic_signals.rs +++ b/game/src/edit/traffic_signals.rs @@ -4,7 +4,7 @@ use crate::game::{msg, State, Transition, WizardState}; use crate::helpers::ID; use crate::render::{draw_signal_phase, DrawOptions, DrawTurn, TrafficSignalDiagram}; use crate::ui::{ShowEverything, UI}; -use ezgui::{hotkey, Choice, Color, EventCtx, GeomBatch, GfxCtx, Key, ModalMenu}; +use ezgui::{hotkey, Choice, Color, EventCtx, GeomBatch, GfxCtx, Key, Line, ModalMenu, Text}; use geom::Duration; use map_model::{ ControlTrafficSignal, EditCmd, IntersectionID, Phase, TurnID, TurnPriority, TurnType, @@ -38,6 +38,7 @@ impl TrafficSignalEditor { hotkey(Key::B), "convert to dedicated pedestrian scramble phase", ), + (hotkey(Key::O), "change signal offset"), (hotkey(Key::Escape), "quit"), ], ctx, @@ -52,7 +53,17 @@ impl TrafficSignalEditor { impl State for TrafficSignalEditor { fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition { + let mut signal = ui.primary.map.get_traffic_signal(self.diagram.i).clone(); + self.menu.event(ctx); + // TODO This really needs to be shown in the diagram! + self.menu.set_info( + ctx, + Text::from(Line(format!( + "Signal offset: {}", + signal.offset.minimal_tostring() + ))), + ); ctx.canvas.handle_event(ctx.input); self.diagram.event(ctx, &mut self.menu); @@ -72,8 +83,6 @@ impl State for TrafficSignalEditor { } } - let mut signal = ui.primary.map.get_traffic_signal(self.diagram.i).clone(); - if let Some(id) = self.icon_selected { let phase = &mut signal.phases[self.diagram.current_phase()]; // Just one key to toggle between the 3 states @@ -115,11 +124,13 @@ impl State for TrafficSignalEditor { } if self.menu.action("change phase duration") { - return Transition::Push(make_change_phase_duration( + return Transition::Push(change_phase_duration( signal.phases[self.diagram.current_phase()].duration, )); + } else if self.menu.action("change signal offset") { + return Transition::Push(change_offset(signal.offset)); } else if self.menu.action("choose a preset signal") { - return Transition::Push(make_change_preset(self.diagram.i)); + return Transition::Push(change_preset(self.diagram.i)); } else if self.menu.action("reset to original") { signal = ControlTrafficSignal::get_possible_policies(&ui.primary.map, self.diagram.i) .remove(0) @@ -256,7 +267,7 @@ fn change_traffic_signal(signal: ControlTrafficSignal, ui: &mut UI, ctx: &mut Ev apply_map_edits(&mut ui.primary, &ui.cs, ctx, edits); } -fn make_change_phase_duration(current_duration: Duration) -> Box { +fn change_phase_duration(current_duration: Duration) -> Box { WizardState::new(Box::new(move |wiz, ctx, _| { let new_duration = wiz.wrap(ctx).input_usize_prefilled( "How long should this phase be (seconds)?", @@ -273,7 +284,28 @@ fn make_change_phase_duration(current_duration: Duration) -> Box { })) } -fn make_change_preset(i: IntersectionID) -> Box { +fn change_offset(current_duration: Duration) -> Box { + WizardState::new(Box::new(move |wiz, ctx, _| { + let new_duration = wiz.wrap(ctx).input_usize_prefilled( + "What should the offset of this traffic signal be (seconds)?", + format!("{}", current_duration.inner_seconds() as usize), + )?; + Some(Transition::PopWithData(Box::new(move |state, ui, ctx| { + let mut editor = state.downcast_mut::().unwrap(); + let mut signal = ui.primary.map.get_traffic_signal(editor.diagram.i).clone(); + signal.offset = Duration::seconds(new_duration as f64); + change_traffic_signal(signal, ui, ctx); + editor.diagram = TrafficSignalDiagram::new( + editor.diagram.i, + editor.diagram.current_phase(), + ui, + ctx, + ); + }))) + })) +} + +fn change_preset(i: IntersectionID) -> Box { WizardState::new(Box::new(move |wiz, ctx, ui| { let (_, new_signal) = wiz.wrap(ctx) diff --git a/map_model/src/traffic_signals.rs b/map_model/src/traffic_signals.rs index 51a57c3633..2a1038020e 100644 --- a/map_model/src/traffic_signals.rs +++ b/map_model/src/traffic_signals.rs @@ -8,6 +8,7 @@ use std::collections::BTreeSet; pub struct ControlTrafficSignal { pub id: IntersectionID, pub phases: Vec, + pub offset: Duration, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] @@ -66,7 +67,7 @@ impl ControlTrafficSignal { cycle_length += p.duration; } - let mut now_offset = now % cycle_length; + let mut now_offset = (now + self.offset) % cycle_length; for (idx, p) in self.phases.iter().enumerate() { if now_offset < p.duration { return (idx, p, p.duration - now_offset); @@ -160,6 +161,7 @@ impl ControlTrafficSignal { let ts = ControlTrafficSignal { id: intersection, phases, + offset: Duration::ZERO, }; // This must succeed ts.validate(map).unwrap() @@ -187,7 +189,11 @@ impl ControlTrafficSignal { let phases = make_phases(map, i, phases); - let ts = ControlTrafficSignal { id: i, phases }; + let ts = ControlTrafficSignal { + id: i, + phases, + offset: Duration::ZERO, + }; ts.validate(map).ok() } @@ -236,7 +242,11 @@ impl ControlTrafficSignal { ], ); - let ts = ControlTrafficSignal { id: i, phases }; + let ts = ControlTrafficSignal { + id: i, + phases, + offset: Duration::ZERO, + }; ts.validate(map).ok() } @@ -278,7 +288,11 @@ impl ControlTrafficSignal { ], ); - let ts = ControlTrafficSignal { id: i, phases }; + let ts = ControlTrafficSignal { + id: i, + phases, + offset: Duration::ZERO, + }; ts.validate(map).ok() } @@ -319,7 +333,11 @@ impl ControlTrafficSignal { ], ); - let ts = ControlTrafficSignal { id: i, phases }; + let ts = ControlTrafficSignal { + id: i, + phases, + offset: Duration::ZERO, + }; ts.validate(map).ok() } @@ -371,7 +389,11 @@ impl ControlTrafficSignal { ], ); - let ts = ControlTrafficSignal { id: i, phases }; + let ts = ControlTrafficSignal { + id: i, + phases, + offset: Duration::ZERO, + }; ts.validate(map).ok() } @@ -397,6 +419,7 @@ impl ControlTrafficSignal { let ts = ControlTrafficSignal { id: i, phases: vec![all_walk, all_yield], + offset: Duration::ZERO, }; // This must succeed ts.validate(map).unwrap() @@ -430,7 +453,11 @@ impl ControlTrafficSignal { phases.push(phase); } } - let ts = ControlTrafficSignal { id: i, phases }; + let ts = ControlTrafficSignal { + id: i, + phases, + offset: Duration::ZERO, + }; ts.validate(map).ok() }