1use std::sync::Arc;
18
19use crate::nature::seeding::Cells;
20use crate::terrain::{CellType, HeightGrid};
21
22#[derive(Debug, Clone, Default)]
24pub struct TerraformTemplate {
25 pub floor: Option<Arc<Vec<[f32; 3]>>>,
27 pub floor_mask: u32,
29 pub ceiling: Option<Arc<Vec<[f32; 3]>>>,
31 pub ceiling_mask: u32,
33}
34
35#[derive(Debug, Clone, Default, PartialEq)]
38pub struct TerraformState {
39 pub raised: Vec<[f32; 3]>,
41 pub cells: Vec<i32>,
43}
44
45#[derive(Debug, Clone, Copy, PartialEq)]
47pub enum Terraform {
48 Dig(usize),
50 Undo(usize),
52 Quarry {
54 entity: usize,
56 floor: f32,
58 claim: bool,
60 },
61}
62
63const HEIGHT_SCALE: f32 = 256.0;
65
66fn cell_type(cells: &Cells, x: f32, y: f32) -> u8 {
67 let n = cells.size as i32;
68 let half = (n >> 1) as f32;
69 let cx = ((x + half) as i32).clamp(0, n - 1);
70 let cy = ((y + half) as i32).clamp(0, n - 1);
71 cells.types[(cy * n + cx) as usize].0
72}
73
74#[allow(clippy::too_many_arguments)]
82pub fn reshape(
83 cells: &Cells,
84 floors: &crate::world::floor::Floors,
85 heights: &mut [i16],
86 width: usize,
87 vertices: &[[f32; 3]],
88 mask: u32,
89 yaw: f32,
90 position: [f32; 3],
91 ceiling: bool,
92 raised: &mut Vec<[f32; 3]>,
93) -> bool {
94 let model = crate::math::Transform::of_model(yaw, position);
95 let mut changed = false;
96 for &v in vertices {
97 let t = model.apply(v);
99 let q = [t[0] + 0.25, t[1] + 0.25, t[2] + 0.0];
100 let ground = {
101 let grid = HeightGrid { width, heights };
102 floors.ground(cells.size, &grid, q[0], q[1])
103 };
104 let types = [
105 cell_type(cells, q[0], q[1]),
106 cell_type(cells, q[0] - 1.0, q[1]),
107 cell_type(cells, q[0] - 1.0, q[1] - 1.0),
108 cell_type(cells, q[0] - 1.0, q[1]),
109 ];
110 let bits = types.iter().fold(0u32, |b, &t| b | 1 << (t & 0x1f));
111 if !mask & bits != 0 {
112 continue;
113 }
114 let moves = if ceiling {
115 ground < q[2]
116 } else {
117 q[2] < ground
118 };
119 if !moves {
120 continue;
121 }
122 let half = (width as i32 >> 1) as f32;
123 let (hx, hy) = ((q[0] + half) as i32, (q[1] + half) as i32);
124 if hx < 0 || hy < 0 || width as i32 <= hx || width as i32 <= hy {
125 continue;
126 }
127 heights[hy as usize * width + hx as usize] = (q[2] * HEIGHT_SCALE) as i32 as i16;
128 changed = true;
129 if ceiling {
130 raised.push([q[0], q[1], ground]);
131 }
132 }
133 changed
134}
135
136pub fn harden(cells: &mut Cells, rect: (i32, i32, i32, i32), kept: &mut Vec<i32>) {
140 let n = cells.size as i32;
141 let (x0, y0, w, h) = rect;
142 for y in y0..y0 + h {
143 for x in x0..x0 + w {
144 let i = n * y + x;
145 if cells.types[i as usize] == CellType::ROUGH {
146 cells.set_type(i as usize, CellType::HIGH);
147 kept.push(i);
148 }
149 }
150 }
151}
152
153pub fn undo(cells: &mut Cells, heights: &mut [i16], width: usize, state: &TerraformState) -> bool {
157 let half = (width as i32 >> 1) as f32;
158 for p in &state.raised {
159 let (hx, hy) = ((p[0] + half) as i32, (p[1] + half) as i32);
160 if 0 <= hx && 0 <= hy && hx < width as i32 && hy < width as i32 {
161 heights[hy as usize * width + hx as usize] = (p[2] * HEIGHT_SCALE) as i32 as i16;
162 }
163 }
164 for &i in &state.cells {
165 cells.set_type(i as usize, CellType::ROUGH);
166 }
167 !state.raised.is_empty()
168}
169
170pub fn flatten(heights: &mut [i16], width: usize, cells: usize, rect: [i32; 4]) -> bool {
176 let off = ((width as i32 - 1) - cells as i32) >> 1;
177 let [x0, y0, x1, y1] = rect;
178 let mut changed = false;
179 for y in y0..=y1 {
180 for x in x0..=x1 {
181 let (gx, gy) = (x + off, y + off);
182 if gx < 0 || gy < 0 || width as i32 <= gx || width as i32 <= gy {
183 continue;
184 }
185 let h = &mut heights[gy as usize * width + gx as usize];
186 changed |= *h != 0;
187 *h = 0;
188 }
189 }
190 changed
191}
192
193#[cfg(test)]
194mod tests {
195 use super::flatten;
196
197 #[test]
198 fn flattening_zeroes_the_cells_low_corners() {
199 let width = 9;
201 let mut heights = vec![100i16; width * width];
202 assert!(flatten(&mut heights, width, 4, [1, 1, 2, 2]));
203 for y in 0..width {
204 for x in 0..width {
205 let zero = (3..=4).contains(&x) && (3..=4).contains(&y);
206 assert_eq!(heights[y * width + x] == 0, zero, "({x}, {y})");
207 }
208 }
209 assert!(!flatten(&mut heights, width, 4, [1, 1, 2, 2]));
210 }
211}