diff --git a/game/src/abtest/mod.rs b/game/src/abtest/mod.rs index 73e82264fa..27855eaf7b 100644 --- a/game/src/abtest/mod.rs +++ b/game/src/abtest/mod.rs @@ -3,6 +3,7 @@ pub mod setup; use crate::common::{AgentTools, CommonState}; use crate::debug::DebugMode; use crate::game::{State, Transition}; +use crate::options; use crate::render::MIN_ZOOM_FOR_DETAIL; use crate::ui::{PerMapUI, UI}; use abstutil::Timer; @@ -52,6 +53,7 @@ impl ABTestMode { (hotkey(Key::Escape), "quit"), (lctrl(Key::D), "debug mode"), (hotkey(Key::F1), "take a screenshot"), + (None, "options"), ], 0.2, ctx, @@ -130,6 +132,9 @@ impl State for ABTestMode { if self.general_tools.action("take a screenshot") { return Transition::KeepWithMode(EventLoopMode::ScreenCaptureCurrentShot); } + if self.general_tools.action("options") { + return Transition::Push(options::open_panel()); + } if self.menu.action("swap") { let secondary = ui.secondary.take().unwrap(); diff --git a/game/src/debug/mod.rs b/game/src/debug/mod.rs index a2635503b0..d53a851639 100644 --- a/game/src/debug/mod.rs +++ b/game/src/debug/mod.rs @@ -10,6 +10,7 @@ mod routes; use crate::common::CommonState; use crate::game::{msg, State, Transition, WizardState}; use crate::helpers::ID; +use crate::options; use crate::render::MIN_ZOOM_FOR_DETAIL; use crate::ui::{ShowLayers, ShowObject, UI}; use abstutil::Timer; @@ -60,6 +61,7 @@ impl DebugMode { vec![ (hotkey(Key::Escape), "return to previous mode"), (hotkey(Key::F1), "take a screenshot"), + (None, "options"), ], 0.2, ctx, @@ -121,6 +123,9 @@ impl State for DebugMode { if self.general_tools.action("take a screenshot") { return Transition::KeepWithMode(EventLoopMode::ScreenCaptureCurrentShot); } + if self.general_tools.action("options") { + return Transition::Push(options::open_panel()); + } self.all_routes.event(ui, &mut self.menu, ctx); match ui.primary.current_selection { diff --git a/game/src/edit/mod.rs b/game/src/edit/mod.rs index 20990ccd7d..d643f631da 100644 --- a/game/src/edit/mod.rs +++ b/game/src/edit/mod.rs @@ -6,6 +6,7 @@ use crate::common::{CommonState, Warping}; use crate::debug::DebugMode; use crate::game::{State, Transition, WizardState}; use crate::helpers::{ColorScheme, ID}; +use crate::options; use crate::render::{ DrawIntersection, DrawLane, DrawOptions, DrawRoad, Renderable, MIN_ZOOM_FOR_DETAIL, }; @@ -53,6 +54,7 @@ impl EditMode { vec![ (lctrl(Key::D), "debug mode"), (hotkey(Key::F1), "take a screenshot"), + (None, "options"), ], 0.2, ctx, @@ -169,6 +171,9 @@ impl State for EditMode { if self.general_tools.action("take a screenshot") { return Transition::KeepWithMode(EventLoopMode::ScreenCaptureCurrentShot); } + if self.general_tools.action("options") { + return Transition::Push(options::open_panel()); + } if ui.primary.map.get_edits().dirty && self.menu.action("save edits") { return Transition::Push(WizardState::new(Box::new(|wiz, ctx, ui| { diff --git a/game/src/main.rs b/game/src/main.rs index 638ee48cd7..3c8bb14a4a 100644 --- a/game/src/main.rs +++ b/game/src/main.rs @@ -7,6 +7,7 @@ mod game; mod helpers; mod managed; mod mission; +mod options; mod pregame; mod render; mod sandbox; diff --git a/game/src/mission/scenario.rs b/game/src/mission/scenario.rs index 73533e63f5..0a08bb4d53 100644 --- a/game/src/mission/scenario.rs +++ b/game/src/mission/scenario.rs @@ -2,6 +2,7 @@ use crate::common::{CommonState, ObjectColorer, ObjectColorerBuilder, Warping}; use crate::game::{State, Transition, WizardState}; use crate::helpers::ID; use crate::mission::pick_time_range; +use crate::options; use crate::sandbox::{GameplayMode, SandboxMode}; use crate::ui::{ShowEverything, UI}; use abstutil::{prettyprint_usize, Counter, MultiMap, WeightedUsizeChoice}; @@ -129,6 +130,7 @@ impl ScenarioManager { vec![ (hotkey(Key::Escape), "quit"), (hotkey(Key::F1), "take a screenshot"), + (None, "options"), ], 0.2, ctx, @@ -182,6 +184,8 @@ impl State for ScenarioManager { return Transition::Pop; } else if self.general_tools.action("take a screenshot") { return Transition::KeepWithMode(EventLoopMode::ScreenCaptureCurrentShot); + } else if self.general_tools.action("options") { + return Transition::Push(options::open_panel()); } else if self.menu.action("save") { self.scenario.save(); } else if self.menu.action("edit") { diff --git a/game/src/options.rs b/game/src/options.rs new file mode 100644 index 0000000000..1fcb2c2e41 --- /dev/null +++ b/game/src/options.rs @@ -0,0 +1,53 @@ +use crate::game::{State, Transition, WizardState}; +use ezgui::Choice; + +pub struct Options { + pub traffic_signal_style: TrafficSignalStyle, +} + +impl Options { + pub fn default() -> Options { + Options { + traffic_signal_style: TrafficSignalStyle::GroupArrows, + } + } +} + +#[derive(Clone, PartialEq)] +pub enum TrafficSignalStyle { + GroupArrows, + Icons, + IndividualTurnArrows, +} +impl abstutil::Cloneable for TrafficSignalStyle {} + +pub fn open_panel() -> Box { + WizardState::new(Box::new(move |wiz, ctx, ui| { + let mut wizard = wiz.wrap(ctx); + let (_, traffic_signal_style) = + wizard.choose("How should traffic signals be drawn?", || { + vec![ + Choice::new( + "arrows showing the protected and permitted movements", + TrafficSignalStyle::GroupArrows, + ), + Choice::new( + "icons for movements (like the editor UI)", + TrafficSignalStyle::Icons, + ), + Choice::new( + "arrows showing individual turns (to debug)", + TrafficSignalStyle::IndividualTurnArrows, + ), + ] + })?; + if ui.opts.traffic_signal_style != traffic_signal_style { + ui.opts.traffic_signal_style = traffic_signal_style; + println!("Rerendering traffic signals..."); + for i in ui.primary.draw_map.intersections.iter_mut() { + *i.draw_traffic_signal.borrow_mut() = None; + } + } + Some(Transition::Pop) + })) +} diff --git a/game/src/render/intersection.rs b/game/src/render/intersection.rs index 5d2da46c8c..db8b6d5fb6 100644 --- a/game/src/render/intersection.rs +++ b/game/src/render/intersection.rs @@ -18,7 +18,7 @@ pub struct DrawIntersection { zorder: isize, draw_default: Drawable, - draw_traffic_signal: RefCell>, + pub draw_traffic_signal: RefCell>, // Only for traffic signals pub crosswalks: Vec<(TurnID, GeomBatch)>, } diff --git a/game/src/render/mod.rs b/game/src/render/mod.rs index 4fae6e5a4a..82db90c2e6 100644 --- a/game/src/render/mod.rs +++ b/game/src/render/mod.rs @@ -13,6 +13,7 @@ mod traffic_signal; mod turn; use crate::helpers::{ColorScheme, ID}; +use crate::options::Options; pub use crate::render::area::DrawArea; use crate::render::bike::DrawBike; use crate::render::car::DrawCar; @@ -103,6 +104,7 @@ pub struct DrawCtx<'a> { pub map: &'a Map, pub draw_map: &'a DrawMap, pub sim: &'a Sim, + pub opts: &'a Options, } // TODO Borrow, don't clone, and fix up lots of places storing indirect things to populate diff --git a/game/src/render/traffic_signal.rs b/game/src/render/traffic_signal.rs index f4a2a4cb79..c533052708 100644 --- a/game/src/render/traffic_signal.rs +++ b/game/src/render/traffic_signal.rs @@ -1,3 +1,4 @@ +use crate::options::TrafficSignalStyle; use crate::render::{DrawCtx, DrawTurnGroup, BIG_ARROW_THICKNESS}; use crate::ui::UI; use ezgui::{ @@ -30,60 +31,25 @@ pub fn draw_signal_phase( } } - // TODO Live settings panel to toggle between these 3 styles - if true { - for g in DrawTurnGroup::for_i(i, ctx.map) { - batch.push(ctx.cs.get("turn block background"), g.block.clone()); - let arrow_color = match phase.get_priority_of_group(g.id) { - TurnPriority::Protected => ctx.cs.get("turn protected by traffic signal"), - TurnPriority::Yield => ctx - .cs - .get("turn that can yield by traffic signal") - .alpha(1.0), - TurnPriority::Banned => ctx.cs.get("turn not in current phase"), - }; - batch.push(arrow_color, g.arrow.clone()); - } - } else if true { - for g in &phase.protected_groups { - if g.crosswalk.is_none() { - batch.push( - protected_color, - signal.turn_groups[g] - .geom - .make_arrow(BIG_ARROW_THICKNESS * 2.0) - .unwrap(), - ); - } - } - for g in &phase.yield_groups { - if g.crosswalk.is_none() { - batch.extend( - yield_color, - signal.turn_groups[g] - .geom - .make_arrow_outline(BIG_ARROW_THICKNESS * 2.0, BIG_ARROW_THICKNESS / 2.0) - .unwrap(), - ); - } - } - } else { - // For debugging, can still show individual turns - for turn in ctx.map.get_turns_in_intersection(i) { - if turn.between_sidewalks() { - continue; - } - match phase.get_priority_of_turn(turn.id, signal) { - TurnPriority::Protected => { + match ctx.opts.traffic_signal_style { + TrafficSignalStyle::GroupArrows => { + for g in &phase.protected_groups { + if g.crosswalk.is_none() { batch.push( protected_color, - turn.geom.make_arrow(BIG_ARROW_THICKNESS * 2.0).unwrap(), + signal.turn_groups[g] + .geom + .make_arrow(BIG_ARROW_THICKNESS * 2.0) + .unwrap(), ); } - TurnPriority::Yield => { + } + for g in &phase.yield_groups { + if g.crosswalk.is_none() { batch.extend( yield_color, - turn.geom + signal.turn_groups[g] + .geom .make_arrow_outline( BIG_ARROW_THICKNESS * 2.0, BIG_ARROW_THICKNESS / 2.0, @@ -91,7 +57,47 @@ pub fn draw_signal_phase( .unwrap(), ); } - TurnPriority::Banned => {} + } + } + TrafficSignalStyle::Icons => { + for g in DrawTurnGroup::for_i(i, ctx.map) { + batch.push(ctx.cs.get("turn block background"), g.block.clone()); + let arrow_color = match phase.get_priority_of_group(g.id) { + TurnPriority::Protected => ctx.cs.get("turn protected by traffic signal"), + TurnPriority::Yield => ctx + .cs + .get("turn that can yield by traffic signal") + .alpha(1.0), + TurnPriority::Banned => ctx.cs.get("turn not in current phase"), + }; + batch.push(arrow_color, g.arrow.clone()); + } + } + TrafficSignalStyle::IndividualTurnArrows => { + for turn in ctx.map.get_turns_in_intersection(i) { + if turn.between_sidewalks() { + continue; + } + match phase.get_priority_of_turn(turn.id, signal) { + TurnPriority::Protected => { + batch.push( + protected_color, + turn.geom.make_arrow(BIG_ARROW_THICKNESS * 2.0).unwrap(), + ); + } + TurnPriority::Yield => { + batch.extend( + yield_color, + turn.geom + .make_arrow_outline( + BIG_ARROW_THICKNESS * 2.0, + BIG_ARROW_THICKNESS / 2.0, + ) + .unwrap(), + ); + } + TurnPriority::Banned => {} + } } } } diff --git a/game/src/sandbox/mod.rs b/game/src/sandbox/mod.rs index 2bf339e41b..39c8621807 100644 --- a/game/src/sandbox/mod.rs +++ b/game/src/sandbox/mod.rs @@ -11,6 +11,7 @@ use crate::edit::EditMode; use crate::edit::{apply_map_edits, save_edits}; use crate::game::{msg, State, Transition, WizardState}; use crate::helpers::ID; +use crate::options; use crate::pregame::main_menu; use crate::ui::{ShowEverything, UI}; use abstutil::Timer; @@ -48,6 +49,7 @@ impl SandboxMode { (hotkey(Key::Escape), "back to title screen"), (lctrl(Key::D), "debug mode"), (hotkey(Key::F1), "take a screenshot"), + (None, "options"), ], 0.3, ctx, @@ -211,6 +213,9 @@ impl State for SandboxMode { if self.general_tools.action("take a screenshot") { return Transition::KeepWithMode(EventLoopMode::ScreenCaptureCurrentShot); } + if self.general_tools.action("options") { + return Transition::Push(options::open_panel()); + } if let Some(ID::Building(b)) = ui.primary.current_selection { let cars = ui diff --git a/game/src/ui.rs b/game/src/ui.rs index 7ce3b9267e..8ef20c93ca 100644 --- a/game/src/ui.rs +++ b/game/src/ui.rs @@ -1,4 +1,5 @@ use crate::helpers::{ColorScheme, ID}; +use crate::options::Options; use crate::render::{ draw_vehicle, AgentCache, AgentColorScheme, DrawCtx, DrawMap, DrawOptions, DrawPedCrowd, DrawPedestrian, Renderable, MIN_ZOOM_FOR_DETAIL, @@ -17,6 +18,7 @@ pub struct UI { pub cs: ColorScheme, pub agent_cs: AgentColorScheme, pub prebaked: Analytics, + pub opts: Options, } impl UI { @@ -120,6 +122,7 @@ impl UI { cs, agent_cs: AgentColorScheme::VehicleTypes, prebaked, + opts: Options::default(), } } @@ -136,6 +139,7 @@ impl UI { map: &self.primary.map, draw_map: &self.primary.draw_map, sim: &self.primary.sim, + opts: &self.opts, } }