1use std::collections::{HashMap, HashSet};
2
3use abstutil::MultiMap;
4use geom::{Angle, Circle, Distance, PolyLine, Pt2D, Speed};
5use map_gui::ID;
6use map_model::{BuildingID, Direction, IntersectionID, LaneType, RoadID};
7use widgetry::EventCtx;
8
9use crate::controls::InstantController;
10use crate::App;
11
12const ZOOM: f64 = 10.0;
13
14pub struct Player {
15 pos: Pt2D,
16 facing: Angle,
17 on: On,
18 bldgs_along_road: BuildingsAlongRoad,
19
20 controls: InstantController,
21}
22
23impl Player {
24 pub fn new(ctx: &mut EventCtx, app: &App, start: IntersectionID) -> Player {
25 ctx.canvas.cam_zoom = ZOOM;
26 let pos = app.map.get_i(start).polygon.center();
27 ctx.canvas.center_on_map_pt(pos);
28
29 Player {
30 pos,
31 facing: Angle::ZERO,
32 on: On::Intersection(start),
33 bldgs_along_road: BuildingsAlongRoad::new(app),
34
35 controls: InstantController::new(),
36 }
37 }
38
39 pub fn update_with_speed(
41 &mut self,
42 ctx: &mut EventCtx,
43 app: &App,
44 speed: Speed,
45 ) -> Vec<BuildingID> {
46 if let Some((dx, dy)) = self.controls.displacement(ctx, speed) {
47 self.apply_displacement(ctx, app, dx, dy, true)
48 } else {
50 Vec::new()
51 }
52 }
53
54 fn pos_to_on(&self, app: &App, pos: Pt2D) -> Option<On> {
55 let (valid_roads, valid_intersections) = self.on.get_connections(app);
58
59 for id in app
61 .draw_map
62 .get_matching_objects(Circle::new(pos, Distance::meters(3.0)).get_bounds())
63 {
64 if let ID::Intersection(i) = id {
65 if valid_intersections.contains(&i) && app.map.get_i(i).polygon.contains_pt(pos) {
66 return Some(On::Intersection(i));
67 }
68 } else if let ID::Road(r) = id {
69 let road = app.map.get_r(r);
70 if valid_roads.contains(&r)
71 && !road.is_light_rail()
72 && road.get_thick_polygon().contains_pt(pos)
73 {
74 let pt_on_center_line = road.center_pts.project_pt(pos);
76 if let Some((dist, _)) = road.center_pts.dist_along_of_point(pt_on_center_line)
77 {
78 let dir = if dist < road.length() / 2.0 {
82 Direction::Fwd
83 } else {
84 Direction::Back
85 };
86 return Some(On::Road(r, dist, dir));
87 } else {
88 error!(
89 "{} snapped to {} on {}, but dist_along_of_point failed",
90 pos, pt_on_center_line, r
91 );
92 return None;
93 }
94 }
95 }
96 }
97 None
98 }
99
100 fn apply_displacement(
101 &mut self,
102 ctx: &mut EventCtx,
103 app: &App,
104 dx: f64,
105 dy: f64,
106 recurse: bool,
107 ) -> Vec<BuildingID> {
108 let new_pos = self.pos.offset(dx, dy);
109 let mut buildings_passed = Vec::new();
110 if let Some(mut new_on) = self.pos_to_on(app, new_pos) {
111 self.pos = new_pos;
112 ctx.canvas.center_on_map_pt(self.pos);
113
114 if let (On::Road(r1, dist1, _), On::Road(r2, dist2, _)) =
115 (self.on.clone(), new_on.clone())
116 {
117 if r1 == r2 {
118 buildings_passed.extend(self.bldgs_along_road.query_range(r1, dist1, dist2));
120 if dist1 < dist2 {
121 new_on = On::Road(r2, dist2, Direction::Fwd);
122 } else {
123 new_on = On::Road(r2, dist2, Direction::Back);
124 }
125 }
126 }
127 self.on = new_on;
128 } else {
129 if recurse {
134 let orig = self.pos;
135 if dx != 0.0 {
136 buildings_passed.extend(self.apply_displacement(ctx, app, dx, 0.0, false));
137 }
138 if dy != 0.0 {
139 buildings_passed.extend(self.apply_displacement(ctx, app, 0.0, dy, false));
140 }
141
142 if self.pos == orig {
144 if true {
145 buildings_passed.extend(self.apply_displacement(ctx, app, -dx, -dy, false));
148 } else {
149 let old_ring = match self.on {
151 On::Intersection(i) => {
152 app.map.get_i(i).polygon.get_outer_ring().clone()
153 }
154 On::Road(r, _, _) => {
155 let road = app.map.get_r(r);
156 road.center_pts.to_thick_ring(road.get_width())
157 }
158 };
159 if let Some(pt) = old_ring
162 .all_intersections(&PolyLine::must_new(vec![self.pos, new_pos]))
163 .get(0)
164 {
165 buildings_passed.extend(self.apply_displacement(
166 ctx,
167 app,
168 pt.x() - self.pos.x(),
169 pt.y() - self.pos.y(),
170 false,
171 ));
172 }
173 }
174 }
175 }
176 }
177
178 if let On::Road(r, dist, dir) = self.on {
180 let (pt, angle) = app.map.get_r(r).center_pts.must_dist_along(dist);
181 self.pos = pt;
182 self.facing = if dir == Direction::Fwd {
183 angle.opposite()
184 } else {
185 angle
186 };
187 ctx.canvas.center_on_map_pt(self.pos);
188 } else {
189 self.facing = self.controls.facing;
190 }
191
192 buildings_passed
193 }
194
195 pub fn get_pos(&self) -> Pt2D {
196 self.pos
197 }
198
199 pub fn get_angle(&self) -> Angle {
200 self.facing
201 }
202
203 pub fn on_good_road(&self, app: &App) -> bool {
205 let roads = match self.on {
206 On::Road(r, _, _) => vec![r],
207 On::Intersection(i) => app.map.get_i(i).roads.iter().cloned().collect(),
208 };
209 for r in roads {
210 for l in &app.map.get_r(r).lanes {
211 if l.lane_type == LaneType::Biking || l.lane_type == LaneType::Bus {
212 return true;
213 }
214 }
215 }
216 false
217 }
218
219 pub fn override_pos(&mut self, pos: Pt2D) {
221 self.pos = pos;
222 }
223}
224
225#[derive(Clone, PartialEq)]
226enum On {
227 Intersection(IntersectionID),
228 Road(RoadID, Distance, Direction),
230}
231
232impl On {
233 fn get_connections(&self, app: &App) -> (HashSet<RoadID>, HashSet<IntersectionID>) {
234 let mut valid_roads = HashSet::new();
235 let mut valid_intersections = HashSet::new();
236 match self {
237 On::Road(r, _, _) => {
238 let r = app.map.get_r(*r);
239 valid_intersections.insert(r.src_i);
240 valid_intersections.insert(r.dst_i);
241 valid_roads.extend(app.map.get_i(r.src_i).roads.clone());
243 valid_roads.extend(app.map.get_i(r.dst_i).roads.clone());
244 }
245 On::Intersection(i) => {
246 let i = app.map.get_i(*i);
247 for r in &i.roads {
248 valid_roads.insert(*r);
249 let r = app.map.get_r(*r);
251 valid_intersections.insert(r.src_i);
252 valid_intersections.insert(r.dst_i);
253 }
254 }
255 }
256 (valid_roads, valid_intersections)
257 }
258}
259
260struct BuildingsAlongRoad {
261 per_road: HashMap<RoadID, Vec<(Distance, BuildingID)>>,
264}
265
266impl BuildingsAlongRoad {
267 fn new(app: &App) -> BuildingsAlongRoad {
268 let mut raw: MultiMap<RoadID, (Distance, BuildingID)> = MultiMap::new();
269 for b in app.map.all_buildings() {
270 let road = app.map.get_parent(b.sidewalk_pos.lane());
272 let dist = match app.map.get_l(b.sidewalk_pos.lane()).dir {
273 Direction::Fwd => b.sidewalk_pos.dist_along(),
274 Direction::Back => road.length() - b.sidewalk_pos.dist_along(),
275 };
276 raw.insert(road.id, (dist, b.id));
277 }
278
279 let mut per_road = HashMap::new();
280 for (road, list) in raw.consume() {
281 per_road.insert(road, list.into_iter().collect());
283 }
284
285 BuildingsAlongRoad { per_road }
286 }
287
288 fn query_range(&self, road: RoadID, dist1: Distance, dist2: Distance) -> Vec<BuildingID> {
289 if dist1 > dist2 {
290 return self.query_range(road, dist2, dist1);
291 }
292
293 let mut results = Vec::new();
294 if let Some(list) = self.per_road.get(&road) {
295 for (dist, b) in list {
297 if *dist >= dist1 && *dist <= dist2 {
298 results.push(*b);
299 }
300 }
301 }
302 results
303 }
304}