mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 23:43:25 +03:00
converting mission mode
This commit is contained in:
parent
859429a493
commit
281a0ffcb9
@ -49,7 +49,7 @@ impl State for PickABTest {
|
||||
(Transition::Keep, EventLoopMode::InputOnly)
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
fn draw(&self, g: &mut GfxCtx, _: &UI) {
|
||||
self.wizard.draw(g);
|
||||
}
|
||||
}
|
||||
@ -83,7 +83,7 @@ impl State for ABTestSetup {
|
||||
(Transition::Keep, EventLoopMode::InputOnly)
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
fn draw(&self, g: &mut GfxCtx, _: &UI) {
|
||||
self.scroller.draw(g);
|
||||
self.menu.draw(g);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::abtest::setup::PickABTest;
|
||||
use crate::debug::DebugMode;
|
||||
use crate::edit::EditMode;
|
||||
//use crate::mission::MissionEditMode;
|
||||
use crate::mission::MissionEditMode;
|
||||
use crate::render::DrawOptions;
|
||||
use crate::sandbox::SandboxMode;
|
||||
//use crate::tutorial::TutorialMode;
|
||||
@ -280,7 +280,7 @@ fn splash_screen(
|
||||
x if x == edit => Some(Transition::Push(Box::new(EditMode::new(ctx, ui)))),
|
||||
//x if x == tutorial => break Some(Mode::Tutorial(TutorialMode::new(ctx, ui))),
|
||||
x if x == debug => Some(Transition::Push(Box::new(DebugMode::new(ctx, ui)))),
|
||||
//x if x == mission => break Some(Mode::Mission(MissionEditMode::new(ctx, ui))),
|
||||
x if x == mission => Some(Transition::Push(Box::new(MissionEditMode::new(ctx, ui)))),
|
||||
x if x == abtest => Some(Transition::Push(Box::new(PickABTest::new()))),
|
||||
x if x == about => {
|
||||
if wizard.acknowledge(
|
||||
|
@ -4,7 +4,7 @@ mod debug;
|
||||
mod edit;
|
||||
mod game;
|
||||
mod helpers;
|
||||
//mod mission;
|
||||
mod mission;
|
||||
mod render;
|
||||
mod sandbox;
|
||||
mod state;
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::common::{CommonState, SpeedControls};
|
||||
use crate::mission::trips::{clip_trips, Trip};
|
||||
use crate::state::{State, Transition};
|
||||
use crate::ui::{ShowEverything, UI};
|
||||
use abstutil::prettyprint_usize;
|
||||
use ezgui::{
|
||||
@ -83,9 +84,10 @@ impl TripsVisualizer {
|
||||
fn current_time(&self) -> Duration {
|
||||
self.time_slider.get_percent() * Duration::parse("23:59:59.9").unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
// Returns None if the we're done
|
||||
pub fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Option<EventLoopMode> {
|
||||
impl State for TripsVisualizer {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> (Transition, EventLoopMode) {
|
||||
let time = self.current_time();
|
||||
|
||||
let mut txt = Text::prompt("Trips Visualizer");
|
||||
@ -110,7 +112,7 @@ impl TripsVisualizer {
|
||||
let thirty_mins = Duration::minutes(30);
|
||||
|
||||
if self.menu.action("quit") {
|
||||
return None;
|
||||
return (Transition::Pop, EventLoopMode::InputOnly);
|
||||
} else if time != last_time && self.menu.action("forwards 10 seconds") {
|
||||
self.time_slider
|
||||
.set_percent(ctx, (time + ten_secs) / last_time);
|
||||
@ -135,7 +137,7 @@ impl TripsVisualizer {
|
||||
self.time_slider
|
||||
.set_percent(ctx, ((time + dt) / last_time).min(1.0));
|
||||
} else {
|
||||
return Some(EventLoopMode::InputOnly);
|
||||
return (Transition::Keep, EventLoopMode::InputOnly);
|
||||
}
|
||||
|
||||
// TODO Do this more efficiently. ;)
|
||||
@ -149,13 +151,13 @@ impl TripsVisualizer {
|
||||
.collect();
|
||||
|
||||
if self.speed.is_paused() {
|
||||
Some(EventLoopMode::InputOnly)
|
||||
(Transition::Keep, EventLoopMode::InputOnly)
|
||||
} else {
|
||||
Some(EventLoopMode::Animation)
|
||||
(Transition::Keep, EventLoopMode::Animation)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
let time = self.current_time();
|
||||
let mut batch = GeomBatch::new();
|
||||
for idx in &self.active_trips {
|
||||
|
@ -1,9 +1,11 @@
|
||||
use crate::common::CommonState;
|
||||
use crate::helpers::{rotating_color_total, ID};
|
||||
use crate::state::{State, Transition};
|
||||
use crate::ui::UI;
|
||||
use abstutil::{prettyprint_usize, Timer};
|
||||
use ezgui::{
|
||||
hotkey, Color, EventCtx, GfxCtx, HorizontalAlignment, Key, ModalMenu, Text, VerticalAlignment,
|
||||
hotkey, Color, EventCtx, EventLoopMode, GfxCtx, HorizontalAlignment, Key, ModalMenu, Text,
|
||||
VerticalAlignment,
|
||||
};
|
||||
use geom::{Distance, Polygon, Pt2D};
|
||||
use popdat::{Estimate, PopDat};
|
||||
@ -55,9 +57,9 @@ impl DataVisualizer {
|
||||
current_tract: None,
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the we're done
|
||||
pub fn event(&mut self, ctx: &mut EventCtx, ui: &UI) -> bool {
|
||||
}
|
||||
impl State for DataVisualizer {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> (Transition, EventLoopMode) {
|
||||
let mut txt = Text::prompt("Data Visualizer");
|
||||
if let Some(ref name) = self.current_tract {
|
||||
txt.add_line("Census ".to_string());
|
||||
@ -78,7 +80,7 @@ impl DataVisualizer {
|
||||
|
||||
// TODO Remember which dataset we're showing and don't allow reseting to the same.
|
||||
if self.menu.action("quit") {
|
||||
return true;
|
||||
return (Transition::Pop, EventLoopMode::InputOnly);
|
||||
} else if self.current_dataset != 0 && self.menu.action("household vehicles") {
|
||||
self.current_dataset = 0;
|
||||
} else if self.current_dataset != 1 && self.menu.action("commute times") {
|
||||
@ -101,10 +103,10 @@ impl DataVisualizer {
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
(Transition::Keep, EventLoopMode::InputOnly)
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
for (name, tract) in &self.tracts {
|
||||
let color = if Some(name.clone()) == self.current_tract {
|
||||
ui.cs.get("selected")
|
||||
|
@ -1,8 +1,9 @@
|
||||
use crate::common::CommonState;
|
||||
use crate::helpers::ID;
|
||||
use crate::mission::trips::{clip_trips, Trip, TripEndpt};
|
||||
use crate::state::{State, Transition};
|
||||
use crate::ui::{ShowEverything, UI};
|
||||
use ezgui::{hotkey, Color, EventCtx, GfxCtx, ItemSlider, Key, Text};
|
||||
use ezgui::{hotkey, Color, EventCtx, EventLoopMode, GfxCtx, ItemSlider, Key, Text};
|
||||
use geom::{Circle, Distance, Line, Speed};
|
||||
use map_model::BuildingID;
|
||||
use popdat::psrc;
|
||||
@ -52,9 +53,10 @@ impl TripsVisualizer {
|
||||
bldgs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the we're done
|
||||
pub fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> bool {
|
||||
impl State for TripsVisualizer {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> (Transition, EventLoopMode) {
|
||||
self.slider.event(ctx);
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
@ -68,12 +70,12 @@ impl TripsVisualizer {
|
||||
}
|
||||
|
||||
if self.slider.action("quit") {
|
||||
return true;
|
||||
return (Transition::Pop, EventLoopMode::InputOnly);
|
||||
}
|
||||
false
|
||||
(Transition::Keep, EventLoopMode::InputOnly)
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
let (_, trip) = self.slider.get();
|
||||
let from = trip.from.polygon(&ui.primary.map);
|
||||
let to = trip.to.polygon(&ui.primary.map);
|
||||
|
@ -6,10 +6,8 @@ mod scenario;
|
||||
mod trips;
|
||||
|
||||
use self::trips::{pick_time_range, trips_to_scenario};
|
||||
use crate::game::{GameState, Mode};
|
||||
use crate::render::DrawOptions;
|
||||
use crate::sandbox::SandboxMode;
|
||||
use crate::ui::ShowEverything;
|
||||
use crate::state::{State, Transition};
|
||||
use crate::ui::UI;
|
||||
use abstutil::Timer;
|
||||
use ezgui::{hotkey, EventCtx, EventLoopMode, GfxCtx, Key, ModalMenu, Wizard, WrappedWizard};
|
||||
@ -18,19 +16,7 @@ use map_model::Map;
|
||||
use sim::Scenario;
|
||||
|
||||
pub struct MissionEditMode {
|
||||
state: State,
|
||||
}
|
||||
|
||||
enum State {
|
||||
Exploring(ModalMenu),
|
||||
Neighborhood(neighborhood::NeighborhoodEditor),
|
||||
LoadScenario(Wizard),
|
||||
CreateNewScenario(Wizard),
|
||||
EditScenario(scenario::ScenarioEditor),
|
||||
DataViz(dataviz::DataVisualizer),
|
||||
IndividualTrips(individ_trips::TripsVisualizer),
|
||||
AllTrips(all_trips::TripsVisualizer),
|
||||
TripsToScenario(Wizard),
|
||||
menu: ModalMenu,
|
||||
}
|
||||
|
||||
impl MissionEditMode {
|
||||
@ -39,7 +25,7 @@ impl MissionEditMode {
|
||||
ui.primary.reset_sim();
|
||||
|
||||
MissionEditMode {
|
||||
state: State::Exploring(ModalMenu::new(
|
||||
menu: ModalMenu::new(
|
||||
"Mission Edit Mode",
|
||||
vec![
|
||||
(hotkey(Key::Escape), "quit"),
|
||||
@ -53,170 +39,162 @@ impl MissionEditMode {
|
||||
(None, "create new scenario"),
|
||||
],
|
||||
ctx,
|
||||
)),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event(state: &mut GameState, ctx: &mut EventCtx) -> EventLoopMode {
|
||||
match state.mode {
|
||||
Mode::Mission(ref mut mode) => {
|
||||
match mode.state {
|
||||
State::Exploring(ref mut menu) => {
|
||||
menu.handle_event(ctx, None);
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
impl State for MissionEditMode {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> (Transition, EventLoopMode) {
|
||||
self.menu.handle_event(ctx, None);
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
if menu.action("quit") {
|
||||
state.mode = Mode::SplashScreen(Wizard::new(), None);
|
||||
} else if menu.action("visualize population data") {
|
||||
mode.state =
|
||||
State::DataViz(dataviz::DataVisualizer::new(ctx, &state.ui));
|
||||
} else if menu.action("visualize individual PSRC trips") {
|
||||
mode.state = State::IndividualTrips(
|
||||
individ_trips::TripsVisualizer::new(ctx, &state.ui),
|
||||
);
|
||||
} else if menu.action("visualize all PSRC trips") {
|
||||
mode.state =
|
||||
State::AllTrips(all_trips::TripsVisualizer::new(ctx, &state.ui));
|
||||
} else if menu.action("set up simulation with PSRC trips") {
|
||||
let scenario = trips_to_scenario(
|
||||
ctx,
|
||||
&state.ui,
|
||||
Duration::ZERO,
|
||||
Duration::parse("23:59:59.9").unwrap(),
|
||||
);
|
||||
ctx.loading_screen("instantiate scenario", |_, timer| {
|
||||
scenario.instantiate(
|
||||
&mut state.ui.primary.sim,
|
||||
&state.ui.primary.map,
|
||||
&mut state.ui.primary.current_flags.sim_flags.make_rng(),
|
||||
timer,
|
||||
);
|
||||
state
|
||||
.ui
|
||||
.primary
|
||||
.sim
|
||||
.step(&state.ui.primary.map, Duration::const_seconds(0.1));
|
||||
});
|
||||
state.mode = Mode::Sandbox(SandboxMode::new(ctx));
|
||||
} else if menu.action("create scenario from PSRC trips") {
|
||||
mode.state = State::TripsToScenario(Wizard::new());
|
||||
} else if menu.action("manage neighborhoods") {
|
||||
mode.state = State::Neighborhood(
|
||||
neighborhood::NeighborhoodEditor::PickNeighborhood(Wizard::new()),
|
||||
);
|
||||
} else if menu.action("load scenario") {
|
||||
mode.state = State::LoadScenario(Wizard::new());
|
||||
} else if menu.action("create new scenario") {
|
||||
mode.state = State::CreateNewScenario(Wizard::new());
|
||||
}
|
||||
}
|
||||
State::DataViz(ref mut viz) => {
|
||||
if viz.event(ctx, &state.ui) {
|
||||
mode.state = MissionEditMode::new(ctx, &mut state.ui).state;
|
||||
}
|
||||
}
|
||||
State::IndividualTrips(ref mut viz) => {
|
||||
if viz.event(ctx, &mut state.ui) {
|
||||
mode.state = MissionEditMode::new(ctx, &mut state.ui).state;
|
||||
}
|
||||
}
|
||||
State::AllTrips(ref mut viz) => {
|
||||
if let Some(evmode) = viz.event(ctx, &mut state.ui) {
|
||||
return evmode;
|
||||
} else {
|
||||
mode.state = MissionEditMode::new(ctx, &mut state.ui).state;
|
||||
}
|
||||
}
|
||||
State::TripsToScenario(ref mut wizard) => {
|
||||
if let Some((t1, t2)) = pick_time_range(wizard.wrap(ctx)) {
|
||||
trips_to_scenario(ctx, &state.ui, t1, t2).save();
|
||||
mode.state = MissionEditMode::new(ctx, &mut state.ui).state;
|
||||
} else if wizard.aborted() {
|
||||
mode.state = MissionEditMode::new(ctx, &mut state.ui).state;
|
||||
}
|
||||
}
|
||||
State::Neighborhood(ref mut editor) => {
|
||||
if editor.event(ctx, &state.ui) {
|
||||
mode.state = MissionEditMode::new(ctx, &mut state.ui).state;
|
||||
}
|
||||
}
|
||||
State::LoadScenario(ref mut wizard) => {
|
||||
if let Some(scenario) =
|
||||
load_scenario(&state.ui.primary.map, &mut wizard.wrap(ctx))
|
||||
{
|
||||
mode.state =
|
||||
State::EditScenario(scenario::ScenarioEditor::new(scenario, ctx));
|
||||
} else if wizard.aborted() {
|
||||
mode.state = MissionEditMode::new(ctx, &mut state.ui).state;
|
||||
}
|
||||
}
|
||||
State::CreateNewScenario(ref mut wizard) => {
|
||||
let mut wrapped = wizard.wrap(ctx);
|
||||
if let Some(name) = wrapped.input_string("Name the scenario") {
|
||||
mode.state = State::EditScenario(scenario::ScenarioEditor::new(
|
||||
Scenario {
|
||||
scenario_name: name,
|
||||
map_name: state.ui.primary.map.get_name().to_string(),
|
||||
seed_parked_cars: Vec::new(),
|
||||
spawn_over_time: Vec::new(),
|
||||
border_spawn_over_time: Vec::new(),
|
||||
individ_trips: Vec::new(),
|
||||
},
|
||||
ctx,
|
||||
));
|
||||
} else if wizard.aborted() {
|
||||
mode.state = MissionEditMode::new(ctx, &mut state.ui).state;
|
||||
}
|
||||
}
|
||||
State::EditScenario(ref mut editor) => {
|
||||
if let Some(new_mode) = editor.event(ctx, &mut state.ui) {
|
||||
state.mode = new_mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
EventLoopMode::InputOnly
|
||||
}
|
||||
_ => unreachable!(),
|
||||
if self.menu.action("quit") {
|
||||
return (Transition::Pop, EventLoopMode::InputOnly);
|
||||
} else if self.menu.action("visualize population data") {
|
||||
return (
|
||||
Transition::Push(Box::new(dataviz::DataVisualizer::new(ctx, ui))),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
} else if self.menu.action("visualize individual PSRC trips") {
|
||||
return (
|
||||
Transition::Push(Box::new(individ_trips::TripsVisualizer::new(ctx, ui))),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
} else if self.menu.action("visualize all PSRC trips") {
|
||||
return (
|
||||
Transition::Push(Box::new(all_trips::TripsVisualizer::new(ctx, ui))),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
} else if self.menu.action("set up simulation with PSRC trips") {
|
||||
let scenario = trips_to_scenario(
|
||||
ctx,
|
||||
ui,
|
||||
Duration::ZERO,
|
||||
Duration::parse("23:59:59.9").unwrap(),
|
||||
);
|
||||
ctx.loading_screen("instantiate scenario", |_, timer| {
|
||||
scenario.instantiate(
|
||||
&mut ui.primary.sim,
|
||||
&ui.primary.map,
|
||||
&mut ui.primary.current_flags.sim_flags.make_rng(),
|
||||
timer,
|
||||
);
|
||||
ui.primary
|
||||
.sim
|
||||
.step(&ui.primary.map, Duration::const_seconds(0.1));
|
||||
});
|
||||
return (
|
||||
Transition::Replace(Box::new(SandboxMode::new(ctx))),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
} else if self.menu.action("create scenario from PSRC trips") {
|
||||
return (
|
||||
Transition::Push(Box::new(TripsToScenario {
|
||||
wizard: Wizard::new(),
|
||||
})),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
} else if self.menu.action("manage neighborhoods") {
|
||||
return (
|
||||
Transition::Push(Box::new(neighborhood::NeighborhoodPicker::new())),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
} else if self.menu.action("load scenario") {
|
||||
return (
|
||||
Transition::Push(Box::new(LoadScenario {
|
||||
wizard: Wizard::new(),
|
||||
})),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
} else if self.menu.action("create new scenario") {
|
||||
return (
|
||||
Transition::Push(Box::new(CreateNewScenario {
|
||||
wizard: Wizard::new(),
|
||||
})),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
}
|
||||
(Transition::Keep, EventLoopMode::InputOnly)
|
||||
}
|
||||
|
||||
pub fn draw(state: &GameState, g: &mut GfxCtx) {
|
||||
state.ui.draw(
|
||||
g,
|
||||
DrawOptions::new(),
|
||||
&state.ui.primary.sim,
|
||||
&ShowEverything::new(),
|
||||
);
|
||||
fn draw(&self, g: &mut GfxCtx, _: &UI) {
|
||||
self.menu.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
match state.mode {
|
||||
Mode::Mission(ref mode) => match mode.state {
|
||||
State::Exploring(ref menu) => {
|
||||
menu.draw(g);
|
||||
}
|
||||
State::DataViz(ref viz) => {
|
||||
viz.draw(g, &state.ui);
|
||||
}
|
||||
State::IndividualTrips(ref viz) => {
|
||||
viz.draw(g, &state.ui);
|
||||
}
|
||||
State::AllTrips(ref viz) => {
|
||||
viz.draw(g, &state.ui);
|
||||
}
|
||||
State::TripsToScenario(ref wizard) => {
|
||||
wizard.draw(g);
|
||||
}
|
||||
State::Neighborhood(ref editor) => {
|
||||
editor.draw(g, &state.ui);
|
||||
}
|
||||
State::EditScenario(ref editor) => {
|
||||
editor.draw(g, &state.ui);
|
||||
}
|
||||
State::LoadScenario(ref wizard) | State::CreateNewScenario(ref wizard) => {
|
||||
wizard.draw(g);
|
||||
}
|
||||
},
|
||||
_ => unreachable!(),
|
||||
struct TripsToScenario {
|
||||
wizard: Wizard,
|
||||
}
|
||||
|
||||
impl State for TripsToScenario {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> (Transition, EventLoopMode) {
|
||||
if let Some((t1, t2)) = pick_time_range(self.wizard.wrap(ctx)) {
|
||||
trips_to_scenario(ctx, ui, t1, t2).save();
|
||||
return (Transition::Pop, EventLoopMode::InputOnly);
|
||||
} else if self.wizard.aborted() {
|
||||
return (Transition::Pop, EventLoopMode::InputOnly);
|
||||
}
|
||||
(Transition::Keep, EventLoopMode::InputOnly)
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, _: &UI) {
|
||||
self.wizard.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
struct LoadScenario {
|
||||
wizard: Wizard,
|
||||
}
|
||||
|
||||
impl State for LoadScenario {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> (Transition, EventLoopMode) {
|
||||
if let Some(scenario) = load_scenario(&ui.primary.map, &mut self.wizard.wrap(ctx)) {
|
||||
return (
|
||||
Transition::Replace(Box::new(scenario::ScenarioManager::new(scenario, ctx))),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
} else if self.wizard.aborted() {
|
||||
return (Transition::Pop, EventLoopMode::InputOnly);
|
||||
}
|
||||
(Transition::Keep, EventLoopMode::InputOnly)
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, _: &UI) {
|
||||
self.wizard.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
struct CreateNewScenario {
|
||||
wizard: Wizard,
|
||||
}
|
||||
|
||||
impl State for CreateNewScenario {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> (Transition, EventLoopMode) {
|
||||
let mut wrapped = self.wizard.wrap(ctx);
|
||||
if let Some(name) = wrapped.input_string("Name the scenario") {
|
||||
return (
|
||||
Transition::Replace(Box::new(scenario::ScenarioManager::new(
|
||||
Scenario {
|
||||
scenario_name: name,
|
||||
map_name: ui.primary.map.get_name().to_string(),
|
||||
seed_parked_cars: Vec::new(),
|
||||
spawn_over_time: Vec::new(),
|
||||
border_spawn_over_time: Vec::new(),
|
||||
individ_trips: Vec::new(),
|
||||
},
|
||||
ctx,
|
||||
))),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
} else if self.wizard.aborted() {
|
||||
return (Transition::Pop, EventLoopMode::InputOnly);
|
||||
}
|
||||
(Transition::Keep, EventLoopMode::InputOnly)
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, _: &UI) {
|
||||
self.wizard.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,136 +1,145 @@
|
||||
use crate::state::{State, Transition};
|
||||
use crate::ui::UI;
|
||||
use ezgui::{hotkey, Color, EventCtx, GfxCtx, Key, ModalMenu, Wizard, WrappedWizard};
|
||||
use ezgui::{
|
||||
hotkey, Color, EventCtx, EventLoopMode, GfxCtx, Key, ModalMenu, Wizard, WrappedWizard,
|
||||
};
|
||||
use geom::{Circle, Distance, Line, Polygon, Pt2D};
|
||||
use map_model::{Map, NeighborhoodBuilder};
|
||||
|
||||
const POINT_RADIUS: Distance = Distance::const_meters(10.0);
|
||||
|
||||
pub enum NeighborhoodEditor {
|
||||
PickNeighborhood(Wizard),
|
||||
// Option<usize> is the point currently being hovered over
|
||||
EditNeighborhood(ModalMenu, NeighborhoodBuilder, Option<usize>),
|
||||
// usize is the point being moved
|
||||
MovingPoint(ModalMenu, NeighborhoodBuilder, usize),
|
||||
pub struct NeighborhoodPicker {
|
||||
wizard: Wizard,
|
||||
}
|
||||
|
||||
impl NeighborhoodEditor {
|
||||
fn modal_menu(ctx: &EventCtx, name: &str) -> ModalMenu {
|
||||
ModalMenu::new(
|
||||
&format!("Neighborhood Editor for {}", name),
|
||||
vec![
|
||||
(hotkey(Key::Escape), "quit"),
|
||||
(hotkey(Key::S), "save"),
|
||||
(hotkey(Key::X), "export as an Osmosis polygon filter"),
|
||||
(hotkey(Key::P), "add a new point"),
|
||||
],
|
||||
ctx,
|
||||
)
|
||||
impl NeighborhoodPicker {
|
||||
pub fn new() -> NeighborhoodPicker {
|
||||
NeighborhoodPicker {
|
||||
wizard: Wizard::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl State for NeighborhoodPicker {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> (Transition, EventLoopMode) {
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
if let Some(n) = pick_neighborhood(&ui.primary.map, self.wizard.wrap(ctx)) {
|
||||
return (
|
||||
Transition::Push(Box::new(NeighborhoodEditor {
|
||||
menu: ModalMenu::new(
|
||||
&format!("Neighborhood Editor for {}", n.name),
|
||||
vec![
|
||||
(hotkey(Key::Escape), "quit"),
|
||||
(hotkey(Key::S), "save"),
|
||||
(hotkey(Key::X), "export as an Osmosis polygon filter"),
|
||||
(hotkey(Key::P), "add a new point"),
|
||||
],
|
||||
ctx,
|
||||
),
|
||||
neighborhood: n,
|
||||
mouseover_pt: None,
|
||||
moving_pt: false,
|
||||
})),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
} else if self.wizard.aborted() {
|
||||
return (Transition::Pop, EventLoopMode::InputOnly);
|
||||
}
|
||||
(Transition::Keep, EventLoopMode::InputOnly)
|
||||
}
|
||||
|
||||
// True if done
|
||||
pub fn event(&mut self, ctx: &mut EventCtx, ui: &UI) -> bool {
|
||||
fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
// TODO is this order wrong?
|
||||
self.wizard.draw(g);
|
||||
if let Some(neighborhood) = self.wizard.current_menu_choice::<NeighborhoodBuilder>() {
|
||||
g.draw_polygon(
|
||||
ui.cs.get("neighborhood polygon"),
|
||||
&Polygon::new(
|
||||
&ui.primary
|
||||
.map
|
||||
.get_gps_bounds()
|
||||
.must_convert(&neighborhood.points),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct NeighborhoodEditor {
|
||||
menu: ModalMenu,
|
||||
neighborhood: NeighborhoodBuilder,
|
||||
mouseover_pt: Option<usize>,
|
||||
moving_pt: bool,
|
||||
}
|
||||
|
||||
impl State for NeighborhoodEditor {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> (Transition, EventLoopMode) {
|
||||
let gps_bounds = ui.primary.map.get_gps_bounds();
|
||||
match self {
|
||||
NeighborhoodEditor::PickNeighborhood(ref mut wizard) => {
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
if let Some(n) = pick_neighborhood(&ui.primary.map, wizard.wrap(ctx)) {
|
||||
*self = NeighborhoodEditor::EditNeighborhood(
|
||||
NeighborhoodEditor::modal_menu(ctx, &n.name),
|
||||
n,
|
||||
None,
|
||||
);
|
||||
} else if wizard.aborted() {
|
||||
return true;
|
||||
self.menu.handle_event(ctx, None);
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
if self.moving_pt {
|
||||
if let Some(pt) = ctx
|
||||
.canvas
|
||||
.get_cursor_in_map_space()
|
||||
.and_then(|c| c.to_gps(gps_bounds))
|
||||
{
|
||||
self.neighborhood.points[self.mouseover_pt.unwrap()] = pt;
|
||||
}
|
||||
if ctx.input.key_released(Key::LeftControl) {
|
||||
self.moving_pt = false;
|
||||
}
|
||||
} else {
|
||||
if self.menu.action("quit") {
|
||||
return (Transition::Pop, EventLoopMode::InputOnly);
|
||||
} else if self.neighborhood.points.len() >= 3 && self.menu.action("save") {
|
||||
self.neighborhood.save();
|
||||
} else if self.neighborhood.points.len() >= 3
|
||||
&& self.menu.action("export as an Osmosis polygon filter")
|
||||
{
|
||||
self.neighborhood.save_as_osmosis().unwrap();
|
||||
} else if let Some(pt) = ctx
|
||||
.canvas
|
||||
.get_cursor_in_map_space()
|
||||
.and_then(|c| c.to_gps(gps_bounds))
|
||||
{
|
||||
if self.menu.action("add a new point") {
|
||||
self.neighborhood.points.push(pt);
|
||||
}
|
||||
}
|
||||
NeighborhoodEditor::EditNeighborhood(ref mut menu, ref mut n, ref mut current_idx) => {
|
||||
menu.handle_event(ctx, None);
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
if menu.action("quit") {
|
||||
return true;
|
||||
} else if n.points.len() >= 3 && menu.action("save") {
|
||||
n.save();
|
||||
return true;
|
||||
} else if n.points.len() >= 3 && menu.action("export as an Osmosis polygon filter")
|
||||
{
|
||||
n.save_as_osmosis().unwrap();
|
||||
} else if let Some(pt) = ctx
|
||||
.canvas
|
||||
.get_cursor_in_map_space()
|
||||
.and_then(|c| c.to_gps(gps_bounds))
|
||||
{
|
||||
if menu.action("add a new point") {
|
||||
n.points.push(pt);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(cursor) = ctx.canvas.get_cursor_in_map_space() {
|
||||
*current_idx = n.points.iter().position(|pt| {
|
||||
Circle::new(
|
||||
Pt2D::from_gps(*pt, gps_bounds).unwrap(),
|
||||
POINT_RADIUS / ctx.canvas.cam_zoom,
|
||||
)
|
||||
.contains_pt(cursor)
|
||||
});
|
||||
} else {
|
||||
*current_idx = None;
|
||||
}
|
||||
if let Some(idx) = current_idx {
|
||||
// TODO mouse dragging might be more intuitive, but it's unclear how to
|
||||
// override part of canvas.handle_event
|
||||
if ctx
|
||||
.input
|
||||
.key_pressed(Key::LeftControl, "hold to move this point")
|
||||
{
|
||||
*self = NeighborhoodEditor::MovingPoint(
|
||||
NeighborhoodEditor::modal_menu(ctx, &n.name),
|
||||
n.clone(),
|
||||
*idx,
|
||||
);
|
||||
}
|
||||
}
|
||||
if let Some(cursor) = ctx.canvas.get_cursor_in_map_space() {
|
||||
self.mouseover_pt = self.neighborhood.points.iter().position(|pt| {
|
||||
Circle::new(
|
||||
Pt2D::from_gps(*pt, gps_bounds).unwrap(),
|
||||
POINT_RADIUS / ctx.canvas.cam_zoom,
|
||||
)
|
||||
.contains_pt(cursor)
|
||||
});
|
||||
} else {
|
||||
self.mouseover_pt = None;
|
||||
}
|
||||
NeighborhoodEditor::MovingPoint(ref mut menu, ref mut n, idx) => {
|
||||
menu.handle_event(ctx, None);
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
if let Some(pt) = ctx
|
||||
.canvas
|
||||
.get_cursor_in_map_space()
|
||||
.and_then(|c| c.to_gps(gps_bounds))
|
||||
{
|
||||
n.points[*idx] = pt;
|
||||
}
|
||||
if ctx.input.key_released(Key::LeftControl) {
|
||||
*self = NeighborhoodEditor::EditNeighborhood(
|
||||
NeighborhoodEditor::modal_menu(ctx, &n.name),
|
||||
n.clone(),
|
||||
Some(*idx),
|
||||
);
|
||||
}
|
||||
// TODO mouse dragging might be more intuitive, but it's unclear how to
|
||||
// override part of canvas.handle_event
|
||||
if self.mouseover_pt.is_some()
|
||||
&& ctx
|
||||
.input
|
||||
.key_pressed(Key::LeftControl, "hold to move this point")
|
||||
{
|
||||
self.moving_pt = true;
|
||||
}
|
||||
}
|
||||
false
|
||||
(Transition::Keep, EventLoopMode::InputOnly)
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
let (raw_pts, current_idx) = match self {
|
||||
NeighborhoodEditor::PickNeighborhood(wizard) => {
|
||||
// TODO is this order wrong?
|
||||
wizard.draw(g);
|
||||
if let Some(neighborhood) = wizard.current_menu_choice::<NeighborhoodBuilder>() {
|
||||
(&neighborhood.points, None)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
NeighborhoodEditor::EditNeighborhood(_, n, current_idx) => (&n.points, *current_idx),
|
||||
NeighborhoodEditor::MovingPoint(_, n, current_idx) => (&n.points, Some(*current_idx)),
|
||||
};
|
||||
let gps_bounds = ui.primary.map.get_gps_bounds();
|
||||
let pts: Vec<Pt2D> = gps_bounds.must_convert(&raw_pts);
|
||||
fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
let pts: Vec<Pt2D> = ui
|
||||
.primary
|
||||
.map
|
||||
.get_gps_bounds()
|
||||
.must_convert(&self.neighborhood.points);
|
||||
|
||||
if pts.len() == 2 {
|
||||
g.draw_line(
|
||||
@ -147,7 +156,7 @@ impl NeighborhoodEditor {
|
||||
);
|
||||
}
|
||||
for (idx, pt) in pts.iter().enumerate() {
|
||||
let color = if Some(idx) == current_idx {
|
||||
let color = if Some(idx) == self.mouseover_pt {
|
||||
ui.cs.get_def("neighborhood point to move", Color::CYAN)
|
||||
} else if idx == pts.len() - 1 {
|
||||
ui.cs
|
||||
@ -158,13 +167,7 @@ impl NeighborhoodEditor {
|
||||
g.draw_circle(color, &Circle::new(*pt, POINT_RADIUS / g.canvas.cam_zoom));
|
||||
}
|
||||
|
||||
match self {
|
||||
NeighborhoodEditor::EditNeighborhood(ref menu, _, _)
|
||||
| NeighborhoodEditor::MovingPoint(ref menu, _, _) => {
|
||||
menu.draw(g);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
self.menu.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,23 +1,26 @@
|
||||
use crate::game::Mode;
|
||||
use crate::mission::{input_time, MissionEditMode};
|
||||
use crate::mission::input_time;
|
||||
use crate::sandbox::SandboxMode;
|
||||
use crate::state::{State, Transition};
|
||||
use crate::ui::UI;
|
||||
use abstutil::WeightedUsizeChoice;
|
||||
use ezgui::{hotkey, EventCtx, GfxCtx, Key, LogScroller, ModalMenu, Wizard, WrappedWizard};
|
||||
use ezgui::{
|
||||
hotkey, EventCtx, EventLoopMode, GfxCtx, Key, LogScroller, ModalMenu, Wizard, WrappedWizard,
|
||||
};
|
||||
use geom::Duration;
|
||||
use map_model::{IntersectionID, Map, Neighborhood};
|
||||
use sim::{BorderSpawnOverTime, OriginDestination, Scenario, SeedParkedCars, SpawnOverTime};
|
||||
|
||||
pub enum ScenarioEditor {
|
||||
ManageScenario(ModalMenu, Scenario, LogScroller),
|
||||
EditScenario(Scenario, Wizard),
|
||||
pub struct ScenarioManager {
|
||||
menu: ModalMenu,
|
||||
scenario: Scenario,
|
||||
scroller: LogScroller,
|
||||
}
|
||||
|
||||
impl ScenarioEditor {
|
||||
pub fn new(scenario: Scenario, ctx: &mut EventCtx) -> ScenarioEditor {
|
||||
impl ScenarioManager {
|
||||
pub fn new(scenario: Scenario, ctx: &mut EventCtx) -> ScenarioManager {
|
||||
let scroller = LogScroller::new(scenario.scenario_name.clone(), scenario.describe());
|
||||
ScenarioEditor::ManageScenario(
|
||||
ModalMenu::new(
|
||||
ScenarioManager {
|
||||
menu: ModalMenu::new(
|
||||
&format!("Scenario Editor for {}", scenario.scenario_name),
|
||||
vec![
|
||||
(hotkey(Key::Escape), "quit"),
|
||||
@ -29,58 +32,73 @@ impl ScenarioEditor {
|
||||
),
|
||||
scenario,
|
||||
scroller,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl State for ScenarioManager {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> (Transition, EventLoopMode) {
|
||||
self.menu.handle_event(ctx, None);
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
if self.menu.action("save") {
|
||||
self.scenario.save();
|
||||
} else if self.menu.action("edit") {
|
||||
return (
|
||||
Transition::Push(Box::new(ScenarioEditor {
|
||||
scenario: self.scenario.clone(),
|
||||
wizard: Wizard::new(),
|
||||
})),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
} else if self.menu.action("instantiate") {
|
||||
ctx.loading_screen("instantiate scenario", |_, timer| {
|
||||
self.scenario.instantiate(
|
||||
&mut ui.primary.sim,
|
||||
&ui.primary.map,
|
||||
&mut ui.primary.current_flags.sim_flags.make_rng(),
|
||||
timer,
|
||||
);
|
||||
ui.primary.sim.step(&ui.primary.map, Duration::seconds(0.1));
|
||||
});
|
||||
return (
|
||||
Transition::Replace(Box::new(SandboxMode::new(ctx))),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
} else if self.scroller.event(&mut ctx.input) {
|
||||
return (Transition::Pop, EventLoopMode::InputOnly);
|
||||
}
|
||||
(Transition::Keep, EventLoopMode::InputOnly)
|
||||
}
|
||||
|
||||
pub fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Option<Mode> {
|
||||
match self {
|
||||
ScenarioEditor::ManageScenario(ref mut menu, scenario, ref mut scroller) => {
|
||||
menu.handle_event(ctx, None);
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
if menu.action("save") {
|
||||
scenario.save();
|
||||
} else if menu.action("edit") {
|
||||
*self = ScenarioEditor::EditScenario(scenario.clone(), Wizard::new());
|
||||
} else if menu.action("instantiate") {
|
||||
ctx.loading_screen("instantiate scenario", |_, timer| {
|
||||
scenario.instantiate(
|
||||
&mut ui.primary.sim,
|
||||
&ui.primary.map,
|
||||
&mut ui.primary.current_flags.sim_flags.make_rng(),
|
||||
timer,
|
||||
);
|
||||
ui.primary.sim.step(&ui.primary.map, Duration::seconds(0.1));
|
||||
});
|
||||
return Some(Mode::Sandbox(SandboxMode::new(ctx)));
|
||||
} else if scroller.event(&mut ctx.input) {
|
||||
return Some(Mode::Mission(MissionEditMode::new(ctx, ui)));
|
||||
}
|
||||
}
|
||||
ScenarioEditor::EditScenario(ref mut scenario, ref mut wizard) => {
|
||||
if let Some(()) = edit_scenario(&ui.primary.map, scenario, wizard.wrap(ctx)) {
|
||||
// TODO autosave, or at least make it clear there are unsaved edits
|
||||
*self = ScenarioEditor::new(scenario.clone(), ctx);
|
||||
} else if wizard.aborted() {
|
||||
*self = ScenarioEditor::new(scenario.clone(), ctx);
|
||||
}
|
||||
}
|
||||
fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
self.scroller.draw(g);
|
||||
self.menu.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
struct ScenarioEditor {
|
||||
scenario: Scenario,
|
||||
wizard: Wizard,
|
||||
}
|
||||
|
||||
impl State for ScenarioEditor {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> (Transition, EventLoopMode) {
|
||||
if let Some(()) = edit_scenario(&ui.primary.map, &mut self.scenario, self.wizard.wrap(ctx))
|
||||
{
|
||||
// TODO autosave, or at least make it clear there are unsaved edits
|
||||
// TODO Need to Refresh the scenario in our parent!
|
||||
return (Transition::Pop, EventLoopMode::InputOnly);
|
||||
} else if self.wizard.aborted() {
|
||||
return (Transition::Pop, EventLoopMode::InputOnly);
|
||||
}
|
||||
None
|
||||
(Transition::Keep, EventLoopMode::InputOnly)
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
match self {
|
||||
ScenarioEditor::ManageScenario(ref menu, _, scroller) => {
|
||||
scroller.draw(g);
|
||||
menu.draw(g);
|
||||
}
|
||||
ScenarioEditor::EditScenario(_, wizard) => {
|
||||
if let Some(neighborhood) = wizard.current_menu_choice::<Neighborhood>() {
|
||||
g.draw_polygon(ui.cs.get("neighborhood polygon"), &neighborhood.polygon);
|
||||
}
|
||||
wizard.draw(g);
|
||||
}
|
||||
fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
if let Some(neighborhood) = self.wizard.current_menu_choice::<Neighborhood>() {
|
||||
g.draw_polygon(ui.cs.get("neighborhood polygon"), &neighborhood.polygon);
|
||||
}
|
||||
self.wizard.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user