banished_game/world/map.rs
1//! The gameplay map: `n × n` cells over the centre of the `2n` terrain (see docs/MAP-CELLS.md).
2
3use crate::math::sin_cos_deg;
4use crate::terrain::{CellType, HeightGrid};
5
6/// The gameplay map: the type of each cell. See [`Map::find_start`] for the start of a new town.
7pub struct Map {
8 /// Cells per side (`n`).
9 pub size: usize,
10 // +0x29
11 /// Cell type (byte) per cell, row-major.
12 pub types: Vec<CellType>,
13}
14
15impl Map {
16 /// Classify every cell from the terrain.
17 ///
18 /// Original: `FUN_1400a43f0`.
19 pub fn new(size: usize, terrain: &HeightGrid) -> Map {
20 Map {
21 size,
22 types: (0..size * size)
23 .map(|i| terrain.natural_cell_type(size, i))
24 .collect(),
25 }
26 }
27
28 /// Every cell within `radius` (`dx² + dy² ≤ radius_sq`) of `(x, y)` is in the map and buildable
29 /// ground.
30 ///
31 /// Original: `FUN_1400a7d50`.
32 pub fn is_clear(&self, x: i32, y: i32, radius: i32, radius_sq: i32) -> bool {
33 let n = self.size as i32;
34 for dy in -radius..=radius {
35 for dx in -radius..=radius {
36 if dx * dx + dy * dy > radius_sq {
37 continue;
38 }
39 let (cx, cy) = (x + dx, y + dy);
40 if cx < 0 || cy < 0 || cx >= n || cy >= n {
41 return false;
42 }
43 if self.types[(cy * n + cx) as usize] != CellType::GROUND {
44 return false;
45 }
46 }
47 }
48 true
49 }
50
51 /// The town's start cell. Rings outward from the map centre, testing `4, 8, 16, …` evenly
52 /// spaced angles per ring and moving out `3r/4` cells each ring. Loops forever (as the original
53 /// does) if no clear area exists.
54 ///
55 /// Original: `FUN_1400a7bf0`.
56 pub fn find_start(&self, radius: i32) -> (i32, i32) {
57 let centre = self.size as i32 >> 1;
58 let mut count = 4;
59 let mut dist = 0.0f32;
60 loop {
61 for i in 0..count {
62 let angle = i as f32 * (360.0 / count as f32);
63 let (sin, cos) = sin_cos_deg(angle);
64 let x = (cos * dist) as i32 + centre;
65 let y = (sin * dist) as i32 + centre;
66 if self.is_clear(x, y, radius, radius * radius) {
67 return (x, y);
68 }
69 }
70 count *= 2;
71 dist += ((radius * 3) / 4) as f32;
72 }
73 }
74}