Commit | Line | Data |
---|---|---|
79914631 | 1 | use core::render::Renderer; |
953b4c96 | 2 | use geometry::{Point, Dimension, Intersection, Angle, ToAngle, supercover_line}; |
79914631 | 3 | use sprites::SpriteManager; |
1f42d724 TW |
4 | use std::rc::Rc; |
5 | use {point, dimen}; | |
79914631 TW |
6 | |
7 | mod lvlgen; | |
8 | ||
9 | pub use self::lvlgen::LevelGenerator; | |
10 | ||
11 | ////////// LEVEL /////////////////////////////////////////////////////////////// | |
12 | ||
13 | #[derive(Default)] | |
14 | pub struct Level { | |
e570927a | 15 | pub gravity: Point<f64>, |
37f3e1ed | 16 | pub grid: Grid<bool>, |
2e679e6f | 17 | walls: Vec<Rc<WallRegion>>, |
1f42d724 | 18 | wall_grid: Grid<Vec<Rc<WallEdge>>>, |
79914631 TW |
19 | } |
20 | ||
21 | impl Level { | |
d59c7f04 | 22 | pub fn new(gravity: Point<f64>, grid: Grid<bool>, mut walls: Vec<WallRegion>) -> Self { |
1f42d724 TW |
23 | let size = (2560, 1440); // TODO: get actual size from walls or something |
24 | let wall_grid = Level::build_wall_grid(&mut walls, &size.into()); | |
d59c7f04 | 25 | dbg!(&wall_grid.scale); |
1f42d724 TW |
26 | Level { |
27 | gravity, | |
28 | grid, | |
2e679e6f | 29 | walls: walls.into_iter().map(|i| Rc::new(i)).collect(), |
1f42d724 TW |
30 | wall_grid, |
31 | } | |
32 | } | |
33 | ||
34 | /// Creates a grid of wall edges for fast lookup | |
d01df1fc | 35 | fn build_wall_grid(walls: &mut Vec<WallRegion>, lvlsize: &Dimension<usize>) -> Grid<Vec<Rc<WallEdge>>> { |
1f42d724 TW |
36 | let size = dimen!(lvlsize.width / 20, lvlsize.height / 20); // TODO: make sure all walls fit within the grid bounds |
37 | let cs = point!(lvlsize.width / size.width, lvlsize.height / size.height); | |
d59c7f04 | 38 | //let cs = point!(scale.width as f64, scale.height as f64); |
8012f86b TW |
39 | let mut grid = Grid { |
40 | cells: vec!(vec!(vec!(); size.height); size.width), | |
41 | size, | |
d59c7f04 | 42 | scale: dimen!(cs.x as f64, cs.y as f64), |
8012f86b | 43 | }; |
1f42d724 TW |
44 | |
45 | for wall in walls { | |
46 | for edge in &wall.edges { | |
8012f86b TW |
47 | for c in grid.grid_coordinates_on_line(edge.p1, edge.p2) { |
48 | grid.cells[c.x][c.y].push(Rc::clone(edge)); | |
1f42d724 TW |
49 | } |
50 | } | |
51 | } | |
52 | ||
8012f86b | 53 | grid |
1f42d724 | 54 | } |
79914631 | 55 | |
7b724ff3 TW |
56 | pub fn render(&mut self, renderer: &mut Renderer, _sprites: &SpriteManager, debug_mode: bool) { |
57 | if debug_mode { | |
58 | // original grid | |
59 | renderer.canvas().set_draw_color((64, 64, 64)); | |
60 | let size = &self.grid.scale; | |
61 | for x in 0..self.grid.size.width { | |
62 | for y in 0..self.grid.size.height { | |
63 | if self.grid.cells[x][y] { | |
64 | renderer.canvas().fill_rect(sdl2::rect::Rect::new( | |
65 | x as i32 * size.width as i32, | |
66 | y as i32 * size.height as i32, | |
67 | size.width as u32, | |
68 | size.height as u32)).unwrap(); | |
69 | } | |
70 | } | |
71 | } | |
72 | ||
73 | // wall grid | |
74 | renderer.canvas().set_draw_color((0, 32, 0)); | |
75 | let size = &self.wall_grid.scale; | |
76 | for x in 0..self.wall_grid.size.width { | |
77 | for y in 0..self.wall_grid.size.height { | |
78 | if !self.wall_grid.cells[x][y].is_empty() { | |
79 | let num = self.wall_grid.cells[x][y].len(); | |
80 | renderer.canvas().set_draw_color((0, 32*num as u8, 0)); | |
81 | renderer.canvas().fill_rect(sdl2::rect::Rect::new( | |
82 | x as i32 * size.width as i32, | |
83 | y as i32 * size.height as i32, | |
84 | size.width as u32, | |
85 | size.height as u32)).unwrap(); | |
86 | } | |
1f42d724 TW |
87 | } |
88 | } | |
1f42d724 | 89 | |
7b724ff3 TW |
90 | // wall normals |
91 | for wall in &self.walls { | |
92 | for e in &wall.edges { | |
93 | let c = (e.p1 + e.p2) / 2.0; | |
94 | let a = (e.p2 - e.p1).to_angle() + std::f64::consts::FRAC_PI_2.radians(); | |
95 | ||
96 | renderer.draw_line( | |
97 | <(i32, i32)>::from(c.to_i32()), | |
98 | <(i32, i32)>::from((c + Point::from(a) * 10.0).to_i32()), | |
99 | (0, 128, 255)); | |
79914631 TW |
100 | } |
101 | } | |
102 | } | |
103 | ||
1f42d724 | 104 | // walls |
79914631 | 105 | for wall in &self.walls { |
1f42d724 | 106 | for e in &wall.edges { |
7b724ff3 TW |
107 | if !debug_mode { |
108 | let c = (e.p1 + e.p2) / 2.0; | |
109 | let a = (e.p2 - e.p1).to_angle() - std::f64::consts::FRAC_PI_2.radians(); | |
110 | ||
111 | renderer.draw_line( | |
112 | <(i32, i32)>::from(c.to_i32()), | |
113 | <(i32, i32)>::from((c + Point::from(a) * 10.0).to_i32()), | |
114 | (255, 128, 0)); | |
115 | ||
116 | renderer.draw_line( | |
117 | <(i32, i32)>::from(e.p1.to_i32()), | |
118 | <(i32, i32)>::from((c + Point::from(a) * 20.0).to_i32()), | |
119 | (96, 48, 0)); | |
120 | renderer.draw_line( | |
121 | <(i32, i32)>::from(e.p2.to_i32()), | |
122 | <(i32, i32)>::from((c + Point::from(a) * 20.0).to_i32()), | |
123 | (96, 48, 0)); | |
124 | } | |
8065e264 | 125 | |
1f42d724 TW |
126 | renderer.draw_line( |
127 | <(i32, i32)>::from(e.p1.to_i32()), | |
128 | <(i32, i32)>::from(e.p2.to_i32()), | |
129 | (255, 255, 0)); | |
79914631 | 130 | } |
79914631 TW |
131 | } |
132 | } | |
60058b91 TW |
133 | |
134 | pub fn intersect_walls(&self, p1: Point<f64>, p2: Point<f64>) -> IntersectResult { | |
8012f86b | 135 | for c in self.wall_grid.grid_coordinates_on_line(p1, p2) { |
b5332019 TW |
136 | for w in &self.wall_grid.cells[c.x][c.y] { |
137 | if let Intersection::Point(p) = Intersection::lines(p1, p2, w.p1, w.p2) { | |
b1075e66 TW |
138 | if w.point_is_in_front(p1) { |
139 | let wall = Wall { | |
140 | region: Rc::clone(&self.walls[w.region]), | |
141 | edge: Rc::clone(w), | |
142 | }; | |
143 | return IntersectResult::Intersection(wall, p) | |
144 | } | |
60058b91 TW |
145 | } |
146 | } | |
147 | } | |
148 | IntersectResult::None | |
149 | } | |
150 | } | |
151 | ||
2e679e6f TW |
152 | pub enum IntersectResult { |
153 | Intersection(Wall, Point<f64>), | |
60058b91 | 154 | None |
79914631 TW |
155 | } |
156 | ||
157 | ////////// GRID //////////////////////////////////////////////////////////////// | |
158 | ||
1f42d724 | 159 | #[derive(Debug, Default)] |
37f3e1ed | 160 | pub struct Grid<T> { |
1f42d724 | 161 | pub size: Dimension<usize>, |
d59c7f04 | 162 | pub scale: Dimension<f64>, |
37f3e1ed | 163 | pub cells: Vec<Vec<T>>, |
79914631 | 164 | } |
1f42d724 | 165 | |
60058b91 | 166 | impl<T> Grid<T> { |
2e679e6f TW |
167 | // pub fn at<C>(&self, c: C) -> Option<&T> |
168 | // where C: Into<(isize, isize)> | |
169 | // { | |
170 | // let c = c.into(); | |
171 | // if c.0 >= 0 && c.0 < self.size.width as isize && c.1 >= 0 && c.1 < self.size.height as isize { | |
172 | // Some(&self.cells[c.0 as usize][c.1 as usize]) | |
173 | // } else { | |
174 | // None | |
175 | // } | |
176 | // } | |
8012f86b TW |
177 | |
178 | pub fn to_grid_coordinate<C>(&self, c: C) -> Option<Point<usize>> | |
179 | where C: Into<(isize, isize)> | |
180 | { | |
181 | let c = c.into(); | |
182 | if c.0 >= 0 && c.0 < self.size.width as isize && c.1 >= 0 && c.1 < self.size.height as isize { | |
183 | Some(point!(c.0 as usize, c.1 as usize)) | |
184 | } else { | |
185 | None | |
186 | } | |
187 | } | |
188 | ||
189 | /// Returns a list of grid coordinates that a line in world coordinates passes through. | |
190 | pub fn grid_coordinates_on_line(&self, p1: Point<f64>, p2: Point<f64>) -> Vec<Point<usize>> { | |
d59c7f04 | 191 | supercover_line(p1 / self.scale, p2 / self.scale) |
8012f86b TW |
192 | .iter() |
193 | .map(|c| self.to_grid_coordinate(*c)) | |
194 | .flatten() | |
195 | .collect() | |
196 | } | |
60058b91 TW |
197 | } |
198 | ||
1f42d724 TW |
199 | ////////// WALL REGION ///////////////////////////////////////////////////////// |
200 | ||
2e679e6f | 201 | #[derive(Debug, Default)] |
1f42d724 TW |
202 | pub struct WallRegion { |
203 | edges: Vec<Rc<WallEdge>>, | |
204 | } | |
205 | ||
206 | impl WallRegion { | |
d01df1fc TW |
207 | pub fn new(points: Vec<Point<f64>>) -> Self { |
208 | let index: RegionIndex = 0; // use as param | |
60058b91 | 209 | let mut edges = Vec::with_capacity(points.len()); |
1f42d724 TW |
210 | |
211 | for i in 0..points.len() { | |
212 | let edge = Rc::new(WallEdge { | |
d01df1fc TW |
213 | region: index, |
214 | id: i, | |
1f42d724 TW |
215 | p1: points[i], |
216 | p2: points[(i + 1) % points.len()], | |
217 | }); | |
218 | edges.push(edge); | |
219 | } | |
220 | ||
d01df1fc | 221 | WallRegion { edges } |
1f42d724 TW |
222 | } |
223 | ||
2e679e6f TW |
224 | fn next(&self, index: EdgeIndex) -> Rc<WallEdge> { |
225 | let index = (index + 1) % self.edges.len(); | |
226 | Rc::clone(&self.edges[index]) | |
227 | } | |
d01df1fc | 228 | |
2e679e6f TW |
229 | fn previous(&self, index: EdgeIndex) -> Rc<WallEdge> { |
230 | let index = (index + self.edges.len() + 1) % self.edges.len(); | |
231 | Rc::clone(&self.edges[index]) | |
232 | } | |
1f42d724 TW |
233 | } |
234 | ||
235 | ////////// WALL EDGE /////////////////////////////////////////////////////////// | |
236 | ||
d01df1fc TW |
237 | type RegionIndex = usize; |
238 | type EdgeIndex = usize; | |
239 | ||
1f42d724 TW |
240 | #[derive(Debug, Default)] |
241 | struct WallEdge { | |
d01df1fc TW |
242 | region: RegionIndex, |
243 | id: EdgeIndex, | |
1f42d724 TW |
244 | pub p1: Point<f64>, |
245 | pub p2: Point<f64>, | |
246 | } | |
60058b91 | 247 | |
b1075e66 TW |
248 | impl WallEdge { |
249 | fn point_is_in_front(&self, p: Point<f64>) -> bool { | |
250 | let cross = (self.p2 - self.p1).cross_product(p - self.p1); | |
251 | cross > 0.0 | |
252 | } | |
253 | } | |
254 | ||
60058b91 TW |
255 | ////////// WALL //////////////////////////////////////////////////////////////// |
256 | ||
2e679e6f TW |
257 | pub struct Wall { |
258 | region: Rc<WallRegion>, | |
259 | edge: Rc<WallEdge>, | |
d01df1fc TW |
260 | } |
261 | ||
2e679e6f TW |
262 | impl Wall { |
263 | #[allow(dead_code)] | |
264 | pub fn next(self) -> Wall { | |
d01df1fc | 265 | Wall { |
2e679e6f | 266 | edge: self.region.next(self.edge.id), |
d01df1fc | 267 | region: self.region, |
d01df1fc TW |
268 | } |
269 | } | |
270 | ||
2e679e6f TW |
271 | #[allow(dead_code)] |
272 | pub fn previous(self) -> Wall { | |
d01df1fc | 273 | Wall { |
2e679e6f | 274 | edge: self.region.previous(self.edge.id), |
d01df1fc | 275 | region: self.region, |
d01df1fc TW |
276 | } |
277 | } | |
8065e264 | 278 | |
40742678 TW |
279 | pub fn normal(&self) -> Angle { |
280 | (self.edge.p2 - self.edge.p1).to_angle() + std::f64::consts::FRAC_PI_2.radians() | |
8065e264 | 281 | } |
60058b91 | 282 | } |