game/render/
bike.rs

1use geom::{ArrowCap, Circle, Distance, Line, PolyLine, Pt2D, Tessellation};
2use map_gui::colors::ColorScheme;
3use map_gui::render::{DrawOptions, OUTLINE_THICKNESS};
4use map_gui::AppLike;
5use map_model::Map;
6use sim::{CarID, DrawCarInput, Intent, Sim};
7use widgetry::{Drawable, GeomBatch, GfxCtx, Prerender};
8
9use crate::render::{grey_out_unhighlighted_people, GameRenderable};
10use crate::ID;
11
12pub struct DrawBike {
13    pub id: CarID,
14    body_circle: Circle,
15    // TODO the turn arrows for bikes look way wrong
16    zorder: isize,
17
18    draw_default: Drawable,
19}
20
21impl DrawBike {
22    pub fn new(
23        input: DrawCarInput,
24        map: &Map,
25        sim: &Sim,
26        prerender: &Prerender,
27        cs: &ColorScheme,
28    ) -> DrawBike {
29        let mut draw_default = GeomBatch::new();
30
31        let body_radius = sim::pedestrian_body_radius();
32        let body_color = grey_out_unhighlighted_people(
33            cs.rotating_color_agents(input.id.id),
34            &input.person,
35            sim,
36        );
37        draw_default.push(
38            cs.bike_frame,
39            input.body.make_polygons(Distance::meters(0.4)),
40        );
41
42        let err = format!("{} on {} has weird body", input.id, input.on);
43        let (body_pos, facing) = input
44            .body
45            .dist_along(0.4 * input.body.length())
46            .expect(&err);
47        let body_circle = Circle::new(body_pos, body_radius);
48        draw_default.push(body_color, body_circle.to_polygon());
49        draw_default.push(
50            cs.ped_head,
51            Circle::new(body_pos, 0.5 * body_radius).to_polygon(),
52        );
53
54        {
55            // Handlebars
56            let (hand_pos, hand_angle) = input
57                .body
58                .dist_along(0.9 * input.body.length())
59                .expect(&err);
60            draw_default.push(
61                cs.bike_frame,
62                Line::must_new(
63                    hand_pos.project_away(body_radius, hand_angle.rotate_degs(90.0)),
64                    hand_pos.project_away(body_radius, hand_angle.rotate_degs(-90.0)),
65                )
66                .make_polygons(Distance::meters(0.1)),
67            );
68
69            // Hands
70            draw_default.push(
71                body_color,
72                Line::must_new(
73                    body_pos.project_away(0.9 * body_radius, facing.rotate_degs(-30.0)),
74                    hand_pos.project_away(0.4 * body_radius, hand_angle.rotate_degs(-90.0)),
75                )
76                .make_polygons(Distance::meters(0.08)),
77            );
78            draw_default.push(
79                body_color,
80                Line::must_new(
81                    body_pos.project_away(0.9 * body_radius, facing.rotate_degs(30.0)),
82                    hand_pos.project_away(0.4 * body_radius, hand_angle.rotate_degs(90.0)),
83                )
84                .make_polygons(Distance::meters(0.08)),
85            );
86        }
87
88        if let Some(t) = input.waiting_for_turn {
89            let angle = map.get_t(t).angle();
90            draw_default.push(
91                cs.turn_arrow,
92                PolyLine::must_new(vec![
93                    body_pos.project_away(body_radius / 2.0, (facing + angle).opposite()),
94                    body_pos.project_away(body_radius / 2.0, facing + angle),
95                ])
96                .make_arrow(Distance::meters(0.15), ArrowCap::Triangle),
97            );
98        }
99
100        if input.intent == Some(Intent::SteepUphill) {
101            let bubble_z = -0.0001;
102            let mut bubble_batch =
103                GeomBatch::load_svg(prerender, "system/assets/map/thought_bubble.svg")
104                    .scale(0.05)
105                    .centered_on(input.body.middle())
106                    .translate(2.0, -3.5)
107                    .set_z_offset(bubble_z);
108            bubble_batch.append(
109                GeomBatch::load_svg(prerender, "system/assets/tools/uphill.svg")
110                    .scale(0.05)
111                    .centered_on(input.body.middle())
112                    .translate(2.2, -4.2)
113                    .set_z_offset(bubble_z),
114            );
115
116            draw_default.append(bubble_batch);
117        }
118
119        let zorder = input
120            .partly_on
121            .into_iter()
122            .chain(vec![input.on])
123            .map(|on| on.get_zorder(map))
124            .max()
125            .unwrap();
126        DrawBike {
127            id: input.id,
128            body_circle,
129            zorder,
130            draw_default: prerender.upload(draw_default),
131        }
132    }
133}
134
135impl GameRenderable for DrawBike {
136    fn get_id(&self) -> ID {
137        ID::Car(self.id)
138    }
139
140    fn draw(&self, g: &mut GfxCtx, _: &dyn AppLike, _: &DrawOptions) {
141        g.redraw(&self.draw_default);
142    }
143
144    fn get_outline(&self, _: &Map) -> Tessellation {
145        Tessellation::from(
146            Circle::new(self.body_circle.center, Distance::meters(2.0))
147                .to_outline(OUTLINE_THICKNESS)
148                .unwrap(),
149        )
150    }
151
152    fn contains_pt(&self, pt: Pt2D, _: &Map) -> bool {
153        Circle::new(self.body_circle.center, Distance::meters(2.0)).contains_pt(pt)
154    }
155
156    fn get_zorder(&self) -> isize {
157        self.zorder
158    }
159}