game/common/
warp.rs

1use std::collections::BTreeMap;
2
3use crate::ID;
4use geom::Pt2D;
5use map_gui::tools::grey_out_map;
6use map_model::{AreaID, BuildingID, IntersectionID, LaneID, ParkingLotID, RoadID, TransitRouteID};
7use sim::{PedestrianID, PersonID, TripID};
8use widgetry::tools::PopupMsg;
9use widgetry::{
10    EventCtx, GfxCtx, Key, Line, Outcome, Panel, State, Text, TextBox, TextExt, Warper, Widget,
11};
12
13use crate::app::{App, PerMap, Transition};
14use crate::info::{OpenTrip, Tab};
15use crate::sandbox::SandboxMode;
16
17const WARP_TO_CAM_ZOOM: f64 = 10.0;
18
19pub struct Warping {
20    warper: Warper,
21    id: Option<ID>,
22}
23
24impl Warping {
25    pub fn new_state(
26        ctx: &EventCtx,
27        pt: Pt2D,
28        target_cam_zoom: Option<f64>,
29        id: Option<ID>,
30        primary: &mut PerMap,
31    ) -> Box<dyn State<App>> {
32        primary.last_warped_from = Some((ctx.canvas.center_to_map_pt(), ctx.canvas.cam_zoom));
33        Box::new(Warping {
34            warper: Warper::new(ctx, pt, target_cam_zoom),
35            id,
36        })
37    }
38}
39
40impl State<App> for Warping {
41    fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
42        if self.warper.event(ctx) {
43            Transition::Keep
44        } else if let Some(id) = self.id.clone() {
45            Transition::Multi(vec![
46                Transition::Pop,
47                Transition::ModifyState(Box::new(move |state, ctx, app| {
48                    // Other states pretty much don't use info panels.
49                    if let Some(ref mut s) = state.downcast_mut::<SandboxMode>() {
50                        let mut actions = s.contextual_actions();
51                        s.controls.common.as_mut().unwrap().launch_info_panel(
52                            ctx,
53                            app,
54                            Tab::from_id(app, id),
55                            &mut actions,
56                        );
57                    }
58                })),
59            ])
60        } else {
61            Transition::Pop
62        }
63    }
64
65    fn draw(&self, _: &mut GfxCtx, _: &App) {}
66}
67
68pub struct DebugWarp {
69    panel: Panel,
70}
71
72impl DebugWarp {
73    pub fn new_state(ctx: &mut EventCtx) -> Box<dyn State<App>> {
74        let c = ctx.style().text_hotkey_color;
75        Box::new(DebugWarp {
76            panel: Panel::new_builder(Widget::col(vec![
77                Widget::row(vec![
78                    Line("Warp to an object by ID")
79                        .small_heading()
80                        .into_widget(ctx),
81                    ctx.style().btn_close_widget(ctx),
82                ]),
83                "Example: r42 is Road #42".text_widget(ctx),
84                // T
85                // his
86                //
87                // i
88                // s
89                //
90                // d
91                // isorienting...
92                Text::from_all(vec![
93                    Line("r").fg(c),
94                    Line("oad, "),
95                    Line("l").fg(c),
96                    Line("ane, "),
97                    Line("i").fg(c),
98                    Line("ntersection, "),
99                    Line("b").fg(c),
100                    Line("uilding, "),
101                    Line("p").fg(c),
102                    Line("edestrian, "),
103                    Line("c").fg(c),
104                    Line("ar, "),
105                    Line("t").fg(c),
106                    Line("rip, "),
107                    Line("P").fg(c),
108                    Line("erson, "),
109                    Line("R").fg(c),
110                    Line("oute, parking "),
111                    Line("L").fg(c),
112                    Line("ot"),
113                ])
114                .into_widget(ctx),
115                Text::from_all(vec![
116                    Line("Or "),
117                    Line("j").fg(c),
118                    Line("ump to the previous position"),
119                ])
120                .into_widget(ctx),
121                TextBox::default_widget(ctx, "input", String::new()),
122                ctx.style()
123                    .btn_outline
124                    .text("Go!")
125                    .hotkey(Key::Enter)
126                    .build_def(ctx),
127            ]))
128            .build(ctx),
129        })
130    }
131}
132
133impl State<App> for DebugWarp {
134    fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
135        match self.panel.event(ctx) {
136            Outcome::Clicked(x) => match x.as_ref() {
137                "close" => Transition::Pop,
138                "Go!" => {
139                    let input = self.panel.text_box("input");
140                    warp_to_id(ctx, app, &input)
141                }
142                _ => unreachable!(),
143            },
144            _ => Transition::Keep,
145        }
146    }
147
148    fn draw(&self, g: &mut GfxCtx, app: &App) {
149        grey_out_map(g, app);
150        self.panel.draw(g);
151    }
152}
153
154pub fn warp_to_id(ctx: &mut EventCtx, app: &mut App, input: &str) -> Transition {
155    if let Some(t) = inner_warp_to_id(ctx, app, input) {
156        t
157    } else {
158        Transition::Replace(PopupMsg::new_state(
159            ctx,
160            "Bad warp ID",
161            vec![format!("{} isn't a valid ID", input)],
162        ))
163    }
164}
165
166pub fn inner_warp_to_id(ctx: &mut EventCtx, app: &mut App, line: &str) -> Option<Transition> {
167    if line.is_empty() {
168        return None;
169    }
170    if line == "j" {
171        if let Some((pt, zoom)) = app.primary.last_warped_from {
172            return Some(Transition::Replace(Warping::new_state(
173                ctx,
174                pt,
175                Some(zoom),
176                None,
177                &mut app.primary,
178            )));
179        }
180        return None;
181    }
182
183    let id = match (&line[1..line.len()]).parse::<usize>() {
184        Ok(idx) => match line.chars().next().unwrap() {
185            'r' => {
186                let r = app.primary.map.maybe_get_r(RoadID(idx))?;
187                ID::Lane(r.lanes[0].id)
188            }
189            'R' => {
190                let r = TransitRouteID(idx);
191                app.primary.map.maybe_get_tr(r)?;
192                return Some(Transition::Multi(vec![
193                    Transition::Pop,
194                    Transition::ModifyState(Box::new(move |state, ctx, app| {
195                        // Other states pretty much don't use info panels.
196                        if let Some(ref mut s) = state.downcast_mut::<SandboxMode>() {
197                            let mut actions = s.contextual_actions();
198                            s.controls.common.as_mut().unwrap().launch_info_panel(
199                                ctx,
200                                app,
201                                Tab::TransitRoute(r),
202                                &mut actions,
203                            );
204                        }
205                    })),
206                ]));
207            }
208            'l' => ID::Lane(LaneID::decode_u32(idx as u32)),
209            'L' => ID::ParkingLot(ParkingLotID(idx)),
210            'i' => ID::Intersection(IntersectionID(idx)),
211            'b' => ID::Building(BuildingID(idx)),
212            'a' => ID::Area(AreaID(idx)),
213            'p' => ID::Pedestrian(PedestrianID(idx)),
214            'P' => {
215                let id = PersonID(idx);
216                app.primary.sim.lookup_person(id)?;
217                return Some(Transition::Multi(vec![
218                    Transition::Pop,
219                    Transition::ModifyState(Box::new(move |state, ctx, app| {
220                        // Other states pretty much don't use info panels.
221                        if let Some(ref mut s) = state.downcast_mut::<SandboxMode>() {
222                            let mut actions = s.contextual_actions();
223                            s.controls.common.as_mut().unwrap().launch_info_panel(
224                                ctx,
225                                app,
226                                Tab::PersonTrips(id, BTreeMap::new()),
227                                &mut actions,
228                            );
229                        }
230                    })),
231                ]));
232            }
233            'c' => {
234                // This one gets more complicated. :)
235                let c = app.primary.sim.lookup_car_id(idx)?;
236                ID::Car(c)
237            }
238            't' => {
239                let trip = TripID(idx);
240                let person = app.primary.sim.trip_to_person(trip)?;
241                return Some(Transition::Multi(vec![
242                    Transition::Pop,
243                    Transition::ModifyState(Box::new(move |state, ctx, app| {
244                        // Other states pretty much don't use info panels.
245                        if let Some(ref mut s) = state.downcast_mut::<SandboxMode>() {
246                            let mut actions = s.contextual_actions();
247                            s.controls.common.as_mut().unwrap().launch_info_panel(
248                                ctx,
249                                app,
250                                Tab::PersonTrips(person, OpenTrip::single(trip)),
251                                &mut actions,
252                            );
253                        }
254                    })),
255                ]));
256            }
257            _ => {
258                return None;
259            }
260        },
261        Err(_) => {
262            return None;
263        }
264    };
265    if let Some(pt) = app.primary.canonical_point(id.clone()) {
266        println!("Warping to {:?}", id);
267        Some(Transition::Replace(Warping::new_state(
268            ctx,
269            pt,
270            Some(WARP_TO_CAM_ZOOM),
271            Some(id),
272            &mut app.primary,
273        )))
274    } else {
275        None
276    }
277}