Skip to main content

banished_terrain/
stages.rs

1//! The five generation stages. Each function names the original it ports.
2
3use super::{Generator, PathPoint};
4use banished_core::noise::PerlinNoise;
5
6/// `Math::Vector2::Normalize` (Runtime 0x18006d0d0): multiply by `1 / length`; zero stays zero.
7fn normalize(v: [f32; 2]) -> [f32; 2] {
8    let len = sqrtf(v[0] * v[0] + v[1] * v[1]);
9    if len == 0.0 {
10        return v;
11    }
12    let inv = 1.0 / len;
13    [v[0] * inv, v[1] * inv]
14}
15
16/// `FUN_1400d43b0` grows its stamp until the logistic weight reaches this (`DAT_140178844`).
17const STAMP_CUTOFF: f32 = 0.99;
18/// Steepness of lake shaping stamps (`DAT_14017887c`).
19const LAKE_STEEPNESS: f32 = 2.25;
20/// Steepness of hill mask stamps (`DAT_140178864`).
21const HILL_STEEPNESS: f32 = 1.75;
22
23/// Parameters of the first river path (`FUN_1400d2560`).
24const FIRST_PATH: [f32; 5] = [2.0, 10.0, 1.25, 4.0, 4.0];
25/// Parameters of branch paths (`FUN_1400d2430`).
26const BRANCH_PATH: [f32; 5] = [1.5, 8.0, 1.25, 1.5, 16.0];
27
28/// Segment intersection test used three times by `FUN_1400d1d20`. Returns true when the open
29/// segments cross. Operand order follows the disassembly.
30fn crosses(p0: [f32; 2], p1: [f32; 2], q0: [f32; 2], q1: [f32; 2]) -> bool {
31    let (qdy, qdx) = (q1[1] - q0[1], q1[0] - q0[0]);
32    let (pdy, pdx) = (p1[1] - p0[1], p1[0] - p0[0]);
33    let (oy, ox) = (p0[1] - q0[1], p0[0] - q0[0]);
34    let den = pdx * qdy - qdx * pdy;
35    if den == 0.0 {
36        return false;
37    }
38    let ua = (qdx * oy - ox * qdy) / den;
39    let ub = (pdx * oy - ox * pdy) / den;
40    ua > 0.0 && 1.0 > ua && ub > 0.0 && 1.0 > ub
41}
42
43fn sqrtf(x: f32) -> f32 {
44    x.sqrt()
45}
46
47/// CRT `powf` (computed in double precision, rounded to float).
48fn powf(base: f32, exp: f32) -> f32 {
49    (base as f64).powf(exp as f64) as f32
50}
51
52impl Generator {
53    /// Stage 1: fill the grid from two noise fields, blended by a logistic falloff from the
54    /// centre.
55    ///
56    /// Original: `FUN_1400d3aa0`.
57    pub fn stage1_noise(&mut self) {
58        let w = self.width();
59        self.grid = vec![0.0; w * w];
60        let a = self.rng.rand_tol(0xffffff);
61        let b = self.rng.rand_tol(0xffffff);
62        let near = PerlinNoise::new(0.5, 5.0, b, a);
63        let a = self.rng.rand_tol(0xffffff);
64        let b = self.rng.rand_tol(0xffffff);
65        let far = PerlinNoise::new(0.25, 5.0, b, a);
66        let half = self.size >> 1;
67        let radius = self.size as f32 * 0.25 + 16.0;
68        for y in 0..w as i32 {
69            let dy = (y - half) * (y - half);
70            let (yf, ynear, yfar) = (y as f32, y as f32 * 0.25, y as f32 * 0.1);
71            let _ = yf;
72            for x in 0..w as i32 {
73                let dist = sqrtf(((x - half) * (x - half) + dy) as f32);
74                let p = powf(1.1, -(dist - radius));
75                let weight = 1.0 / (p + 1.0);
76                let g1 = near.generate(x as f32 * 0.25, ynear);
77                let low = (g1 + 1.0) * 8.0;
78                let g2 = far.generate(x as f32 * 0.1, yfar);
79                let mut high = (g2 + 0.25) * 48.0 + g1;
80                if high < 2.0 {
81                    high = 2.0;
82                }
83                self.grid[y as usize * w + x as usize] = (1.0 - weight) * low + high * weight;
84            }
85        }
86    }
87
88    /// Stage 2: make the first river path, the lake points, then the branch paths.
89    ///
90    /// Original: `FUN_1400d2560`, `FUN_1400d2620`, `FUN_1400d2430`.
91    pub fn stage2_records(&mut self) {
92        while self.paths.is_empty() {
93            self.build_path(self.size + 1, self.size as f32, FIRST_PATH, -1);
94        }
95        self.place_lakes();
96        for _ in 0..self.record.at(0x20) {
97            for _ in 0..1000 {
98                let idx = self.rng.rand_below(self.paths.len() as u32) as i32;
99                if self.build_path(self.size + 1, self.size as f32, BRANCH_PATH, idx) {
100                    break;
101                }
102            }
103        }
104    }
105
106    /// `FUN_1400d19e0`: recursive midpoint displacement between `i0` and `i1`.
107    fn displace(&mut self, pts: &mut [PathPoint], i0: usize, i1: usize, bound: f32, scale: f32) {
108        if i1 - i0 < 2 {
109            return;
110        }
111        let (a, b) = (pts[i0].pos, pts[i1].pos);
112        let mid = [(b[0] + a[0]) * 0.5, (b[1] + a[1]) * 0.5];
113        let d = [b[0] - a[0], b[1] - a[1]];
114        let n = normalize([-d[1], d[0]]);
115        let lo = -bound;
116        let p = loop {
117            let r = self.rng.rand_float_tol() * scale * 0.666;
118            let p = [n[0] * r + mid[0], n[1] * r + mid[1]];
119            let u1 = normalize([b[0] - p[0], b[1] - p[1]]);
120            let u2 = normalize([a[0] - p[0], a[1] - p[1]]);
121            let dot = u2[1] * u1[1] + u1[0] * u2[0];
122            if lo > p[0] || (!(p[0] > lo) && lo > p[1]) {
123                continue;
124            }
125            if p[0] > bound || (!(bound > p[0]) && p[1] > bound) {
126                continue;
127            }
128            if dot > 0.0 {
129                continue;
130            }
131            break p;
132        };
133        let scale = scale * 0.5;
134        let m = ((i1 - i0) >> 1) + i0;
135        pts[m].pos = p;
136        self.displace(pts, i0, m, bound, scale);
137        self.displace(pts, m, i1, bound, scale);
138    }
139
140    /// `FUN_1400d1d20`: build a candidate path of `count` points; append it unless it is
141    /// degenerate or crosses itself or an existing path. `from < 0` starts a new river at the map
142    /// edge, otherwise it branches from `paths[from]`.
143    fn build_path(&mut self, count: i32, extent: f32, params: [f32; 5], from: i32) -> bool {
144        let count = count as usize;
145        let mut pts = vec![
146            PathPoint {
147                pos: [0.0, 0.0],
148                params
149            };
150            count
151        ];
152        let last = count - 1;
153        if from < 0 {
154            pts[0].pos = [0.0, extent * -0.5];
155            match self.rng.rand_below(3) {
156                0 => pts[last].pos = [0.0, extent * 0.5],
157                1 => pts[last].pos = [extent * 0.5, 0.0],
158                2 => pts[last].pos = [extent * -0.5, 0.0],
159                _ => {}
160            }
161            let a = self.rng.rand_float_tol();
162            let b = self.rng.rand_float_tol();
163            pts[count >> 1].pos = [b * extent * 0.125, a * extent * 0.125];
164            self.displace(&mut pts, 0, count >> 1, extent * 0.5, extent * 0.25);
165            self.displace(&mut pts, count >> 1, last, extent * 0.5, extent * 0.25);
166        } else {
167            let base = &self.paths[from as usize];
168            let q = base.len() as i32 / 4;
169            let k = self.rng.rand_range(q, q * 3);
170            pts[0].pos = self.paths[from as usize][k as usize].pos;
171            let v = self.rng.rand_float_tol() * extent * 0.5;
172            match self.rng.rand_below(4) {
173                0 => pts[last].pos = [v, extent * 0.5],
174                1 => pts[last].pos = [extent * 0.5, v],
175                2 => pts[last].pos = [extent * -0.5, v],
176                3 => pts[last].pos = [v, extent * -0.5],
177                _ => {}
178            }
179            self.displace(&mut pts, 0, last, extent * 0.5, extent * 0.25);
180        }
181
182        // Each point's nearest other point must be a neighbour along the path.
183        for i in 0..count {
184            let (mut best, mut best_j) = (1_000_000.0f32, -2i64);
185            for (j, q) in pts.iter().enumerate() {
186                if i == j {
187                    continue;
188                }
189                let d = [pts[i].pos[0] - q.pos[0], pts[i].pos[1] - q.pos[1]];
190                let ls = d[0] * d[0] + d[1] * d[1];
191                if best > ls {
192                    best = ls;
193                    best_j = j as i64;
194                }
195            }
196            if best_j != i as i64 + 1 && best_j != i as i64 - 1 {
197                return false;
198            }
199        }
200        // No crossings with itself or with existing paths.
201        for i in 0..last {
202            let (p0, p1) = (pts[i].pos, pts[i + 1].pos);
203            for j in 0..last {
204                if j == i || j + 1 == i || j == i + 1 {
205                    continue;
206                }
207                if crosses(p0, p1, pts[j].pos, pts[j + 1].pos) {
208                    return false;
209                }
210            }
211            for path in &self.paths {
212                for c in 0..path.len().saturating_sub(1) {
213                    if crosses(p0, p1, path[c].pos, path[c + 1].pos) {
214                        return false;
215                    }
216                }
217            }
218        }
219        self.paths.push(pts);
220        true
221    }
222
223    /// `FUN_1400d2620`: lake centres (`lakes`) and the points shaping each lake (`points`).
224    fn place_lakes(&mut self) {
225        let r = self.record;
226        let count = self.rng.rand_below((r.at(0x2c) - r.at(0x28) + 1) as u32) as i32 + r.at(0x28);
227        let depth = self.rng.rand_below((r.at(0x34) - r.at(0x30)) as u32) as i32 + r.at(0x30);
228        let depth_f = depth as f32;
229        let floor = (depth - r.at(0x40)) as f32;
230        let span = ((r.at(0) >> 1) - 2) as f32 - depth_f;
231        let (rmin, rspan) = (r.at(0x3c) as f32, (r.at(0x40) - r.at(0x3c)) as f32);
232        for _ in 0..count {
233            let a = self.rng.rand_float_tol();
234            let b = self.rng.rand_float_tol();
235            let (y, x) = (span * a, b * span);
236            for _ in 0..r.at(0x38) {
237                let radius = self.rng.rand_float() * rspan + rmin;
238                let spread = self.rng.rand_float() * 0.5 + 0.5;
239                let dy = self.rng.rand_float_tol();
240                let reach = depth_f - radius;
241                let dx = self.rng.rand_float_tol();
242                let v = normalize([dx, dy]);
243                self.points
244                    .push([reach * v[0] * spread + x, reach * v[1] * spread + y, radius]);
245            }
246            self.points.push([x, y, floor]);
247            self.lakes.push([x, y, depth_f]);
248        }
249    }
250
251    /// Stage 3: place the hill circles, then draw the paths, lakes and hills into the grid.
252    ///
253    /// Original: `FUN_1400d2930`, then `FUN_1400d3de0`.
254    pub fn stage3_shape(&mut self) {
255        self.place_hills();
256        self.rasterise_records();
257    }
258
259    /// `FUN_1400d2930`: scatter exclusion circles and hill circles, group overlapping hills, then
260    /// join every group to the first with a bridge of radius-10 circles. Results go to `vec28`.
261    fn place_hills(&mut self) {
262        let r = self.record;
263        let edge = ((r.at(0) >> 1) - 2) as f32;
264        let scatter = |rng: &mut banished_core::random::Random, lo: i32, hi: i32| -> [f32; 3] {
265            let radius = rng.rand_float() * (hi - lo) as f32 + lo as f32;
266            let a = rng.rand_float_tol();
267            let b = rng.rand_float_tol();
268            let span = edge - (radius as i32) as f32;
269            [span * b, span * a, radius]
270        };
271        // Exclusion zones: hills may not sit inside them.
272        let mut exclusions: Vec<[f32; 3]> = Vec::new();
273        for _ in 0..r.at(0x10) {
274            exclusions.push(scatter(&mut self.rng, r.at(0x14), r.at(0x18)));
275        }
276        for _ in 0..r.at(0x04) {
277            let c = scatter(&mut self.rng, r.at(0x08), r.at(0x0c));
278            let blocked = exclusions.iter().any(|e| {
279                let d = [c[0] - e[0], c[1] - e[1]];
280                e[2] > sqrtf(d[1] * d[1] + d[0] * d[0]) + c[2]
281            });
282            if !blocked {
283                self.vec28.push(c);
284            }
285        }
286
287        // Union-find by minimum label over overlapping circles, in creation order.
288        let n = self.vec28.len();
289        let mut labels = vec![-1i32; n];
290        if n == 0 {
291            return;
292        }
293        labels[0] = 0;
294        let mut next = 0i32;
295        for i in 0..n {
296            let mut cur = next;
297            labels[i] = cur;
298            let mut merge = std::collections::BTreeSet::from([cur]);
299            for j in 0..i {
300                let (a, b) = (self.vec28[i], self.vec28[j]);
301                let d = [a[0] - b[0], a[1] - b[1]];
302                let dist = sqrtf(d[1] * d[1] + d[0] * d[0]);
303                if a[2] + b[2] > dist * 1.2 {
304                    merge.insert(labels[j]);
305                    if labels[j] < cur {
306                        cur = labels[j];
307                    }
308                }
309            }
310            let mut max = 0;
311            for l in labels.iter_mut().take(i + 1) {
312                if max < *l {
313                    max = *l;
314                }
315                if merge.contains(l) {
316                    *l = cur;
317                }
318            }
319            next = max + 1;
320        }
321
322        // Members of each group, groups in ascending label order.
323        let unique: std::collections::BTreeSet<i32> = labels.iter().copied().collect();
324        let mut groups: Vec<Vec<usize>> = unique
325            .iter()
326            .map(|&l| (0..n).filter(|&c| labels[c] == l).collect())
327            .collect();
328
329        // Bridge every later group to its nearest member of group 0, then fold it into group 0.
330        for g in 1..groups.len() {
331            let a = groups[g][self.rng.rand_below(groups[g].len() as u32) as usize];
332            let mut b = groups[0][self.rng.rand_below(groups[0].len() as u32) as usize];
333            let mut best = 1_000_000.0f32;
334            for &c in &groups[0] {
335                let d = [
336                    self.vec28[a][0] - self.vec28[c][0],
337                    self.vec28[a][1] - self.vec28[c][1],
338                ];
339                let len = sqrtf(d[0] * d[0] + d[1] * d[1]);
340                if best > len {
341                    best = len;
342                    b = c;
343                }
344            }
345            let delta = [
346                self.vec28[b][0] - self.vec28[a][0],
347                self.vec28[b][1] - self.vec28[a][1],
348            ];
349            let length = sqrtf(delta[0] * delta[0] + delta[1] * delta[1]);
350            let dir = normalize(delta);
351            let mut cur = [self.vec28[a][0], self.vec28[a][1]];
352            let mut t = 0.0f32;
353            while length > t {
354                self.vec28.push([cur[0], cur[1], 10.0]);
355                t += 2.0;
356                cur = [cur[0] + dir[0] * 2.0, cur[1] + dir[1] * 2.0];
357            }
358            let moved = std::mem::take(&mut groups[g]);
359            groups[0].extend(moved);
360        }
361    }
362
363    /// `FUN_1400d43b0`: stamp a logistic disc of radius `r` (steepness `base`) centred on `at`
364    /// into `aux`, keeping the minimum of `value * w` (or the maximum of `value * (1 - w)` when
365    /// `raise`), where `w = 1 / (base^(r - d) + 1)`.
366    fn stamp(&self, aux: &mut [f32], at: [f32; 2], r: f32, base: f32, value: f32, raise: bool) {
367        let mut reach = 1.0f32;
368        let mut w = 1.0 / (powf(base, -(1.0 - r)) + 1.0);
369        while STAMP_CUTOFF > w {
370            reach += 1.0;
371            w = 1.0 / (powf(base, -(reach - r)) + 1.0);
372        }
373        let width = self.size + 1;
374        let half = self.size >> 1;
375        let (cx, cy) = (at[0] as i32 + half, at[1] as i32 + half);
376        let ext = reach as i32 + 1;
377        for dy in -ext..=ext {
378            let row = cy + dy;
379            if row < 0 || row >= width {
380                continue;
381            }
382            for dx in -ext..=ext {
383                let col = cx + dx;
384                if col < 0 || col >= width {
385                    continue;
386                }
387                let d = sqrtf((dx * dx + dy * dy) as f32);
388                let w = 1.0 / (powf(base, -(d - r)) + 1.0);
389                let cell = &mut aux[(row * width + col) as usize];
390                if raise {
391                    let v = (1.0 - w) * value;
392                    if v > *cell {
393                        *cell = v;
394                    }
395                } else {
396                    let v = w * value;
397                    if *cell > v {
398                        *cell = v;
399                    }
400                }
401            }
402        }
403    }
404
405    /// `FUN_1400d3de0`: carve rivers, lakes and hill masks into the grid:
406    /// `grid = carve * grid * hills - raise`.
407    fn rasterise_records(&mut self) {
408        let n = self.grid.len();
409        let (mut carve, mut raise, mut hills) = (vec![1.0f32; n], vec![0.0f32; n], vec![1.0f32; n]);
410        for path in &self.paths {
411            for p in path {
412                let [depth, r1, b1, r2, b2] = p.params;
413                self.stamp(&mut carve, p.pos, r1, b1, 1.0, false);
414                self.stamp(&mut raise, p.pos, r2, b2, depth, true);
415            }
416        }
417        for p in &self.points {
418            self.stamp(&mut raise, [p[0], p[1]], p[2], LAKE_STEEPNESS, 2.0, true);
419        }
420        for c in &self.vec28 {
421            self.stamp(&mut hills, [c[0], c[1]], c[2], HILL_STEEPNESS, 1.0, false);
422        }
423        for i in 0..n {
424            self.grid[i] = carve[i] * self.grid[i] * hills[i] - raise[i];
425        }
426    }
427
428    // The number of walks is record[+0x24].
429    /// Stage 4: walks from random cells inside the map. Each step moves to the lowest of the 3 × 3
430    /// neighbours and moves a part of the height difference.
431    ///
432    /// Original: `FUN_1400d1550`.
433    pub fn stage4_downhill(&mut self) {
434        let w = self.size + 1;
435        for _ in 0..self.record.at(0x24) {
436            let mut x = self.rng.rand_below((w - 2) as u32) as i32 + 1;
437            let mut y = self.rng.rand_below((w - 2) as u32) as i32 + 1;
438            loop {
439                let idx = y * w + x;
440                let h = self.grid[idx as usize];
441                if 0.0 >= h {
442                    break;
443                }
444                let (mut low, mut low_idx, mut lx, mut ly) = (h, idx, -1, -1);
445                for ny in [y - 1, y, y + 1] {
446                    for nx in [x - 1, x, x + 1] {
447                        let v = self.grid[(ny * w + nx) as usize];
448                        if low > v {
449                            (low, low_idx, lx, ly) = (v, ny * w + nx, nx, ny);
450                        }
451                    }
452                }
453                let mut d = (h - low) * 0.0625;
454                if d > 0.8 {
455                    d = 0.8;
456                }
457                self.grid[idx as usize] = (1.0 - d) * h + d * low;
458                self.grid[low_idx as usize] = (1.0 - d) * low + d * h;
459                if low_idx == idx || lx < 1 || ly < 1 || lx >= w - 1 || ly >= w - 1 {
460                    break;
461                }
462                (x, y) = (lx, ly);
463            }
464        }
465    }
466
467    /// Stage 5: carve the rivers and lakes again (without the hill mask), then smooth the grid
468    /// twice.
469    ///
470    /// Original: `FUN_1400d4120`, then `FUN_1400d1820` twice.
471    pub fn stage5_smooth(&mut self) {
472        let n = self.grid.len();
473        let (mut carve, mut raise) = (vec![1.0f32; n], vec![0.0f32; n]);
474        for path in &self.paths {
475            for p in path {
476                let [depth, r1, b1, r2, b2] = p.params;
477                self.stamp(&mut carve, p.pos, r1, b1, 1.0, false);
478                self.stamp(&mut raise, p.pos, r2, b2, depth, true);
479            }
480        }
481        for p in &self.points {
482            self.stamp(&mut raise, [p[0], p[1]], p[2], LAKE_STEEPNESS, 2.0, true);
483        }
484        for i in 0..n {
485            self.grid[i] = carve[i] * self.grid[i] - raise[i];
486        }
487        self.blur();
488        self.blur();
489    }
490
491    /// `FUN_1400d1820`: `(2 * centre + (x-1, y-1) + (x-1, y+1)) / 4` with clamped reads, written
492    /// for `x, y < size`. The temporary buffer's last row and column are never written; the
493    /// original leaves them as whatever its allocator returned, taken here as zero.
494    fn blur(&mut self) {
495        let w = self.size + 1;
496        let clamp = |v: i32| v.clamp(0, w - 1);
497        let mut out = vec![0.0f32; self.grid.len()];
498        for y in 0..self.size {
499            for x in 0..self.size {
500                let at = |gx: i32, gy: i32| self.grid[(clamp(gy) * w + clamp(gx)) as usize];
501                let mut sum = at(x, y) * 2.0;
502                let mut weight = 2.0f32;
503                for dy in [-1, 1] {
504                    sum += at(x - 1, y + dy);
505                    weight += 1.0;
506                }
507                out[(y * w + x) as usize] = sum / weight;
508            }
509        }
510        self.grid = out;
511    }
512}