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