use std::collections::{HashMap, HashSet};
use abstutil::MultiMap;
use geom::{Angle, Circle, Distance, PolyLine, Pt2D, Speed};
use map_gui::ID;
use map_model::{BuildingID, Direction, IntersectionID, LaneType, RoadID};
use widgetry::EventCtx;
use crate::controls::InstantController;
use crate::App;
pub const ZOOM: f64 = 10.0;
pub struct Player {
pos: Pt2D,
on: On,
bldgs_along_road: BuildingsAlongRoad,
controls: InstantController,
}
impl Player {
pub fn new(ctx: &mut EventCtx, app: &App, start: IntersectionID) -> Player {
ctx.canvas.cam_zoom = ZOOM;
let pos = app.map.get_i(start).polygon.center();
ctx.canvas.center_on_map_pt(pos);
Player {
pos,
on: On::Intersection(start),
bldgs_along_road: BuildingsAlongRoad::new(app),
controls: InstantController::new(),
}
}
pub fn update_with_speed(
&mut self,
ctx: &mut EventCtx,
app: &App,
speed: Speed,
) -> Vec<BuildingID> {
if let Some((dx, dy)) = self.controls.displacement(ctx, speed) {
self.apply_displacement(ctx, app, dx, dy, true)
} else {
Vec::new()
}
}
fn pos_to_on(&self, app: &App, pos: Pt2D) -> Option<On> {
let (valid_roads, valid_intersections) = self.on.get_connections(app);
for id in app
.draw_map
.get_matching_objects(Circle::new(pos, Distance::meters(3.0)).get_bounds())
{
if let ID::Intersection(i) = id {
if valid_intersections.contains(&i) && app.map.get_i(i).polygon.contains_pt(pos) {
return Some(On::Intersection(i));
}
} else if let ID::Road(r) = id {
let road = app.map.get_r(r);
if valid_roads.contains(&r)
&& !road.is_light_rail()
&& road.get_thick_polygon(&app.map).contains_pt(pos)
{
let pt_on_center_line = road.center_pts.project_pt(pos);
if let Some((dist, _)) = road.center_pts.dist_along_of_point(pt_on_center_line)
{
return Some(On::Road(r, dist));
} else {
error!(
"{} snapped to {} on {}, but dist_along_of_point failed",
pos, pt_on_center_line, r
);
return None;
}
}
}
}
None
}
fn apply_displacement(
&mut self,
ctx: &mut EventCtx,
app: &App,
dx: f64,
dy: f64,
recurse: bool,
) -> Vec<BuildingID> {
let new_pos = self.pos.offset(dx, dy);
let mut buildings_passed = Vec::new();
if let Some(new_on) = self.pos_to_on(app, new_pos) {
self.pos = new_pos;
ctx.canvas.center_on_map_pt(self.pos);
if let (On::Road(r1, dist1), On::Road(r2, dist2)) = (self.on.clone(), new_on.clone()) {
if r1 == r2 {
buildings_passed.extend(self.bldgs_along_road.query_range(r1, dist1, dist2));
}
}
self.on = new_on;
} else {
if recurse {
let orig = self.pos;
if dx != 0.0 {
buildings_passed.extend(self.apply_displacement(ctx, app, dx, 0.0, false));
}
if dy != 0.0 {
buildings_passed.extend(self.apply_displacement(ctx, app, 0.0, dy, false));
}
if self.pos == orig {
if true {
buildings_passed.extend(self.apply_displacement(ctx, app, -dx, -dy, false));
} else {
let old_ring = match self.on {
On::Intersection(i) => app.map.get_i(i).polygon.clone().into_ring(),
On::Road(r, _) => {
let road = app.map.get_r(r);
road.center_pts
.to_thick_ring(2.0 * road.get_half_width(&app.map))
}
};
if let Some(pt) = old_ring
.all_intersections(&PolyLine::must_new(vec![self.pos, new_pos]))
.get(0)
{
buildings_passed.extend(self.apply_displacement(
ctx,
app,
pt.x() - self.pos.x(),
pt.y() - self.pos.y(),
false,
));
}
}
}
}
}
buildings_passed
}
pub fn get_pos(&self) -> Pt2D {
self.pos
}
pub fn get_angle(&self) -> Angle {
self.controls.facing
}
pub fn on_good_road(&self, app: &App) -> bool {
if let On::Road(r, _) = self.on {
for (_, _, lt) in app.map.get_r(r).lanes_ltr() {
if lt == LaneType::Biking || lt == LaneType::Bus {
return true;
}
}
}
false
}
}
#[derive(Clone, PartialEq)]
enum On {
Intersection(IntersectionID),
Road(RoadID, Distance),
}
impl On {
fn get_connections(&self, app: &App) -> (HashSet<RoadID>, HashSet<IntersectionID>) {
let mut valid_roads = HashSet::new();
let mut valid_intersections = HashSet::new();
match self {
On::Road(r, _) => {
let r = app.map.get_r(*r);
valid_intersections.insert(r.src_i);
valid_intersections.insert(r.dst_i);
valid_roads.extend(app.map.get_i(r.src_i).roads.clone());
valid_roads.extend(app.map.get_i(r.dst_i).roads.clone());
}
On::Intersection(i) => {
let i = app.map.get_i(*i);
for r in &i.roads {
valid_roads.insert(*r);
let r = app.map.get_r(*r);
valid_intersections.insert(r.src_i);
valid_intersections.insert(r.dst_i);
}
}
}
(valid_roads, valid_intersections)
}
}
struct BuildingsAlongRoad {
per_road: HashMap<RoadID, Vec<(Distance, BuildingID)>>,
}
impl BuildingsAlongRoad {
fn new(app: &App) -> BuildingsAlongRoad {
let mut raw: MultiMap<RoadID, (Distance, BuildingID)> = MultiMap::new();
for b in app.map.all_buildings() {
let road = app.map.get_parent(b.sidewalk_pos.lane());
let dist = match road.dir(b.sidewalk_pos.lane()) {
Direction::Fwd => b.sidewalk_pos.dist_along(),
Direction::Back => road.center_pts.length() - b.sidewalk_pos.dist_along(),
};
raw.insert(road.id, (dist, b.id));
}
let mut per_road = HashMap::new();
for (road, list) in raw.consume() {
per_road.insert(road, list.into_iter().collect());
}
BuildingsAlongRoad { per_road }
}
fn query_range(&self, road: RoadID, dist1: Distance, dist2: Distance) -> Vec<BuildingID> {
if dist1 > dist2 {
return self.query_range(road, dist2, dist1);
}
let mut results = Vec::new();
if let Some(list) = self.per_road.get(&road) {
for (dist, b) in list {
if *dist >= dist1 && *dist <= dist2 {
results.push(*b);
}
}
}
results
}
}