Commit | Line | Data |
---|---|---|
1f42d724 TW |
1 | use common::{Point, Dimension}; |
2 | use std::rc::Rc; | |
79914631 TW |
3 | use noise::{NoiseFn, OpenSimplex, Seedable}; |
4 | use rand::Rng; | |
1f42d724 TW |
5 | use super::{Grid, Level, WallRegion}; |
6 | use {point, time_scope}; | |
79914631 TW |
7 | |
8 | ////////// LEVEL GENERATOR ///////////////////////////////////////////////////// | |
9 | ||
9a6d1261 | 10 | #[derive(Debug, Default)] |
79914631 TW |
11 | pub struct LevelGenerator { |
12 | pub seed: u32, | |
13 | pub iterations: u8, | |
9a6d1261 | 14 | pub wall_smooth_radius: u8, |
79914631 TW |
15 | } |
16 | ||
17 | impl LevelGenerator { | |
9a6d1261 TW |
18 | pub fn new(seed: u32) -> Self{ |
19 | LevelGenerator { | |
20 | seed, | |
21 | iterations: 5, | |
22 | wall_smooth_radius: 2, | |
23 | } | |
79914631 TW |
24 | } |
25 | ||
26 | pub fn generate(&self) -> Level { | |
1f42d724 | 27 | dbg!(self); |
9a6d1261 | 28 | time_scope!("level generation"); |
79914631 TW |
29 | |
30 | let cell_size = 20; | |
31 | let (width, height) = (2560 / cell_size, 1440 / cell_size); | |
32 | ||
33 | let mut grid = Grid { | |
1f42d724 TW |
34 | cell_size: (cell_size, cell_size).into(), |
35 | size: (width, height).into(), | |
79914631 TW |
36 | cells: vec!(vec!(true; height); width), |
37 | }; | |
38 | ||
39 | // start with some noise | |
40 | // self.simplex_noise(&mut grid); | |
41 | self.random_noise(&mut grid); | |
42 | ||
43 | // smooth with cellular automata | |
44 | self.smooth(&mut grid); | |
45 | // grid.smooth_until_equilibrium(&mut grid); | |
46 | ||
47 | // increase resolution | |
48 | for _i in 0..1 { | |
49 | grid = self.subdivide(&mut grid); | |
50 | self.smooth(&mut grid); | |
51 | // self.smooth_until_equilibrium(&mut grid); | |
52 | } | |
53 | ||
54 | self.filter_regions(&mut grid); | |
55 | ||
56 | let walls = self.find_walls(&grid); | |
1f42d724 | 57 | Level::new(point!(0.0, 0.1), grid, walls) |
79914631 TW |
58 | } |
59 | ||
60 | #[allow(dead_code)] | |
37f3e1ed | 61 | fn simplex_noise(&self, grid: &mut Grid<bool>) { |
79914631 TW |
62 | let noise = OpenSimplex::new().set_seed(self.seed); |
63 | self.set_each(grid, |x, y| noise.get([x as f64 / 12.0, y as f64 / 12.0]) > 0.055, 1); | |
64 | } | |
65 | ||
66 | #[allow(dead_code)] | |
37f3e1ed | 67 | fn random_noise(&self, grid: &mut Grid<bool>) { |
79914631 TW |
68 | let mut rng: rand::prelude::StdRng = rand::SeedableRng::seed_from_u64(self.seed as u64); |
69 | let noise = OpenSimplex::new().set_seed(self.seed); | |
70 | self.set_each(grid, |_x, _y| rng.gen_range(0, 100) > (45 + (150.0 * noise.get([_x as f64 / 40.0, _y as f64 / 10.0])) as usize), 1); // more horizontal platforms | |
71 | // let w = self.width as f64; | |
72 | // self.set_each(|_x, _y| rng.gen_range(0, 100) > (45 + ((15 * _x) as f64 / w) as usize), 1); // opens up to the right | |
73 | } | |
74 | ||
75 | #[allow(dead_code)] | |
37f3e1ed | 76 | fn smooth(&self, grid: &mut Grid<bool>) { |
79914631 TW |
77 | let distance = 1; |
78 | for _i in 0..self.iterations { | |
1f42d724 TW |
79 | let mut next = vec!(vec!(true; grid.size.height); grid.size.width); |
80 | for x in distance..(grid.size.width - distance) { | |
81 | for y in distance..(grid.size.height - distance) { | |
79914631 TW |
82 | match self.neighbours(&grid.cells, x, y, distance) { |
83 | n if n < 4 => next[x][y] = false, | |
84 | n if n > 4 => next[x][y] = true, | |
85 | _ => next[x][y] = grid.cells[x][y] | |
86 | } | |
87 | } | |
88 | } | |
89 | if grid.cells == next { | |
90 | break; // exit early | |
91 | } else { | |
92 | grid.cells = next; | |
93 | } | |
94 | } | |
95 | } | |
96 | ||
97 | #[allow(dead_code)] | |
37f3e1ed | 98 | fn smooth_until_equilibrium(&self, grid: &mut Grid<bool>) { |
79914631 TW |
99 | let distance = 1; |
100 | let mut count = 0; | |
101 | loop { | |
102 | count += 1; | |
1f42d724 TW |
103 | let mut next = vec!(vec!(true; grid.size.height); grid.size.width); |
104 | for x in distance..(grid.size.width - distance) { | |
105 | for y in distance..(grid.size.height - distance) { | |
79914631 TW |
106 | match self.neighbours(&grid.cells, x, y, distance) { |
107 | n if n < 4 => next[x][y] = false, | |
108 | n if n > 4 => next[x][y] = true, | |
109 | _ => next[x][y] = grid.cells[x][y] | |
110 | }; | |
111 | } | |
112 | } | |
113 | if grid.cells == next { | |
114 | break; | |
115 | } else { | |
116 | grid.cells = next; | |
117 | } | |
118 | } | |
9a6d1261 | 119 | println!(" {} iterations needed", count); |
79914631 TW |
120 | } |
121 | ||
122 | fn neighbours(&self, grid: &Vec<Vec<bool>>, px: usize, py: usize, distance: usize) -> u8 { | |
123 | let mut count = 0; | |
124 | for x in (px - distance)..=(px + distance) { | |
125 | for y in (py - distance)..=(py + distance) { | |
126 | if !(x == px && y == py) && grid[x][y] { | |
127 | count += 1; | |
128 | } | |
129 | } | |
130 | } | |
131 | count | |
132 | } | |
133 | ||
37f3e1ed | 134 | fn set_each<F: FnMut(usize, usize) -> bool>(&self, grid: &mut Grid<bool>, mut func: F, walls: usize) { |
1f42d724 TW |
135 | for x in walls..(grid.size.width - walls) { |
136 | for y in walls..(grid.size.height - walls) { | |
79914631 TW |
137 | grid.cells[x][y] = func(x, y); |
138 | } | |
139 | } | |
140 | } | |
141 | ||
37f3e1ed | 142 | fn subdivide(&self, grid: &mut Grid<bool>) -> Grid<bool> { |
1f42d724 | 143 | let (width, height) = (grid.size.width * 2, grid.size.height * 2); |
79914631 TW |
144 | let mut cells = vec!(vec!(true; height); width); |
145 | for x in 1..(width - 1) { | |
146 | for y in 1..(height - 1) { | |
147 | cells[x][y] = grid.cells[x / 2][y / 2]; | |
148 | } | |
149 | } | |
150 | Grid { | |
1f42d724 TW |
151 | cell_size: (grid.cell_size.width / 2, grid.cell_size.height / 2).into(), |
152 | size: (width, height).into(), | |
79914631 TW |
153 | cells |
154 | } | |
155 | } | |
156 | ||
37f3e1ed | 157 | fn find_regions(&self, grid: &Grid<bool>) -> Vec<Region> { |
9a6d1261 | 158 | time_scope!(" finding all regions"); |
79914631 | 159 | let mut regions = vec!(); |
1f42d724 TW |
160 | let mut marked = vec!(vec!(false; grid.size.height); grid.size.width); |
161 | for x in 0..grid.size.width { | |
162 | for y in 0..grid.size.height { | |
79914631 TW |
163 | if !marked[x][y] { |
164 | regions.push(self.get_region_at_point(grid, x, y, &mut marked)); | |
165 | } | |
166 | } | |
167 | } | |
168 | regions | |
169 | } | |
170 | ||
37f3e1ed | 171 | fn get_region_at_point(&self, grid: &Grid<bool>, x: usize, y: usize, marked: &mut Vec<Vec<bool>>) -> Region { |
79914631 TW |
172 | let value = grid.cells[x][y]; |
173 | let mut cells = vec!(); | |
174 | let mut queue = vec!((x, y)); | |
175 | marked[x][y] = true; | |
176 | ||
177 | while let Some(p) = queue.pop() { | |
178 | cells.push(p); | |
179 | for i in &[(-1, 0), (1, 0), (0, -1), (0, 1)] { | |
180 | let ip = (p.0 as isize + i.0, p.1 as isize + i.1); | |
1f42d724 | 181 | if ip.0 >= 0 && ip.0 < grid.size.width as isize && ip.1 >= 0 && ip.1 < grid.size.height as isize { |
79914631 TW |
182 | let up = (ip.0 as usize, ip.1 as usize); |
183 | if grid.cells[up.0][up.1] == value && !marked[up.0][up.1] { | |
184 | marked[up.0][up.1] = true; | |
185 | queue.push(up); | |
186 | } | |
187 | } | |
188 | } | |
189 | } | |
190 | ||
191 | Region { value, cells } | |
192 | } | |
193 | ||
37f3e1ed | 194 | fn delete_region(&self, grid: &mut Grid<bool>, region: &Region) { |
79914631 TW |
195 | for c in ®ion.cells { |
196 | grid.cells[c.0][c.1] = !region.value; | |
197 | } | |
198 | } | |
199 | ||
37f3e1ed | 200 | fn filter_regions(&self, grid: &mut Grid<bool>) { |
79914631 | 201 | let min_wall_size = 0.0015; |
1f42d724 TW |
202 | println!(" grid size: ({}, {}) = {} cells", grid.size.width, grid.size.height, grid.size.width * grid.size.height); |
203 | println!(" min wall size: {}", (grid.size.width * grid.size.height) as f64 * min_wall_size); | |
79914631 TW |
204 | |
205 | // delete all smaller wall regions | |
206 | for r in self.find_regions(grid).iter().filter(|r| r.value) { | |
1f42d724 | 207 | let percent = r.cells.len() as f64 / (grid.size.width * grid.size.height) as f64; |
79914631 | 208 | if percent < min_wall_size { |
9a6d1261 | 209 | // println!(" delete wall region of size {}", r.cells.len()); |
79914631 TW |
210 | self.delete_region(grid, r); |
211 | } | |
212 | } | |
213 | ||
214 | // delete all rooms but the largest | |
215 | let regions = self.find_regions(grid); // check again, because if a removed room contains a removed wall, the removed wall will become a room | |
216 | let mut rooms: Vec<&Region> = regions.iter().filter(|r| !r.value).collect(); | |
217 | rooms.sort_by_key(|r| r.cells.len()); | |
218 | rooms.reverse(); | |
219 | while rooms.len() > 1 { | |
220 | self.delete_region(grid, rooms.pop().unwrap()); | |
221 | } | |
222 | } | |
223 | ||
1f42d724 | 224 | fn find_walls(&self, grid: &Grid<bool>) -> Vec<Rc<WallRegion>> { |
79914631 TW |
225 | let mut walls = vec!(); |
226 | for r in self.find_regions(&grid) { | |
227 | if r.value { | |
1f42d724 TW |
228 | let outline = r.outline(&grid.cell_size); |
229 | let mut floats = outline.iter().map(|p| point!(p.x as f64, p.y as f64)).collect(); | |
230 | self.smooth_wall(&mut floats, self.wall_smooth_radius as isize); | |
231 | let wall = WallRegion::new(floats); | |
232 | walls.push(wall); | |
79914631 TW |
233 | } |
234 | } | |
235 | walls | |
236 | } | |
1f42d724 TW |
237 | |
238 | fn smooth_wall(&self, points: &mut Vec<Point<f64>>, radius: isize) { | |
239 | let idx = |n| (n as isize + points.len() as isize) as usize % points.len(); | |
240 | let mut new_points = points.clone(); | |
241 | for i in 0..points.len() { | |
242 | new_points[i] = ((i as isize + 1 - radius)..=(i as isize + radius)) // aggregates all points from -radius to +radius | |
243 | .fold(points[idx(i as isize - radius)], |acc, o| acc + points[idx(o)]) // with addition | |
244 | / (radius * 2 + 1) as f64; | |
245 | } | |
246 | *points = new_points; | |
247 | } | |
79914631 TW |
248 | } |
249 | ||
250 | ////////// REGION ////////////////////////////////////////////////////////////// | |
251 | ||
252 | struct Region { | |
253 | value: bool, | |
254 | cells: Vec<(usize, usize)>, | |
255 | } | |
256 | ||
257 | impl Region { | |
258 | fn enclosing_rect(&self) -> (usize, usize, usize, usize) { | |
259 | let mut min = (usize::MAX, usize::MAX); | |
260 | let mut max = (0, 0); | |
261 | for c in &self.cells { | |
262 | if c.0 < min.0 { min.0 = c.0; } | |
263 | else if c.0 > max.0 { max.0 = c.0; } | |
264 | if c.1 < min.1 { min.1 = c.1; } | |
265 | else if c.1 > max.1 { max.1 = c.1; } | |
266 | } | |
267 | (min.0, min.1, 1 + max.0 - min.0, 1 + max.1 - min.1) | |
268 | } | |
269 | ||
1f42d724 | 270 | pub fn outline(&self, scale: &Dimension<usize>) -> Vec<Point<isize>> { |
79914631 TW |
271 | let rect = self.enclosing_rect(); |
272 | let (ox, oy, w, h) = rect; | |
273 | let grid = self.grid(&rect); | |
274 | let mut marked = vec!(vec!(false; h); w); | |
275 | let mut outline = vec!(); | |
276 | let mut directions = vec!((1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1)); // 8 directions rotating right from starting direction right | |
1f42d724 TW |
277 | let multiplier = (scale.width as isize, scale.height as isize); |
278 | let offset = (scale.width as isize / 2, scale.height as isize / 2); | |
79914631 | 279 | |
9768e2bb TW |
280 | let start = self.find_first_point_of_outline(&rect, &grid); |
281 | let mut p = start; | |
79914631 TW |
282 | marked[p.x as usize][p.y as usize] = true; |
283 | loop { | |
1f42d724 | 284 | outline.push((p + (ox as isize, oy as isize)) * multiplier + offset); |
79914631 | 285 | self.find_next_point_of_outline(&grid, &mut p, &mut directions); |
9768e2bb | 286 | if p == start { |
79914631 TW |
287 | break; |
288 | } | |
289 | marked[p.x as usize][p.y as usize] = true; | |
290 | } | |
291 | ||
292 | outline | |
293 | } | |
294 | ||
295 | #[allow(dead_code)] | |
296 | fn print_grid(&self, grid: &Vec<Vec<bool>>) { | |
297 | let w = grid.len(); | |
298 | let h = grid[0].len(); | |
299 | let mut g = vec!(vec!(false; w); h); | |
300 | for x in 0..w { | |
301 | for y in 0..h { | |
302 | g[y][x] = grid[x][y]; | |
303 | } | |
304 | } | |
305 | println!("grid {} x {}", w, h); | |
306 | print!(" "); | |
307 | for n in 0..w { | |
308 | print!("{}", n % 10); | |
309 | } | |
310 | println!(); | |
311 | for (n, row) in g.iter().enumerate() { | |
312 | print!("{:>3}|", n); | |
313 | for col in row { | |
314 | print!("{}", if *col { "#" } else { " " }); | |
315 | } | |
316 | println!("|"); | |
317 | } | |
318 | } | |
319 | ||
320 | fn grid(&self, rect: &(usize, usize, usize, usize)) -> Vec<Vec<bool>> { | |
321 | let (x, y, w, h) = rect; | |
322 | let mut grid = vec!(vec!(false; *h); *w); | |
323 | for c in &self.cells { | |
324 | grid[c.0 - x][c.1 - y] = true; | |
325 | } | |
326 | grid | |
327 | } | |
328 | ||
e570927a | 329 | fn find_first_point_of_outline(&self, rect: &(usize, usize, usize, usize), grid: &Vec<Vec<bool>>) -> Point<isize> { |
79914631 TW |
330 | let (ox, oy, w, h) = rect; |
331 | let is_outer_wall = (ox, oy) == (&0, &0); // we know this is always the outer wall of the level | |
332 | for x in 0..*w { | |
333 | for y in 0..*h { | |
334 | if is_outer_wall && !grid[x][y] { | |
335 | return point!(x as isize, y as isize - 1); // one step back because we're not on a wall tile | |
336 | } | |
337 | else if !is_outer_wall && grid[x][y] { | |
338 | return point!(x as isize, y as isize); | |
339 | } | |
340 | } | |
341 | } | |
342 | panic!("no wall found!"); | |
343 | } | |
344 | ||
e570927a | 345 | fn find_next_point_of_outline(&self, grid: &Vec<Vec<bool>>, p: &mut Point<isize>, directions: &mut Vec<(isize, isize)>) { |
79914631 TW |
346 | directions.rotate_left(2); |
347 | loop { | |
348 | let d = directions[0]; | |
349 | if self.check(*p + d, grid) { | |
350 | *p += d; | |
351 | break; | |
352 | } | |
353 | directions.rotate_right(1); | |
354 | } | |
355 | } | |
356 | ||
e570927a | 357 | fn check(&self, p: Point<isize>, grid: &Vec<Vec<bool>>) -> bool { |
79914631 TW |
358 | if p.x < 0 || p.x >= grid.len() as isize || p.y < 0 || p.y >= grid[0].len() as isize { |
359 | false | |
360 | } else { | |
361 | grid[p.x as usize][p.y as usize] | |
362 | } | |
363 | } | |
364 | } |