1use crate::{
2 block::{ZCache, block_from_structure},
3 column::{ColumnGen, ColumnSample},
4 index::IndexRef,
5 land::Land,
6 layer::spot::Spot,
7 sim::{SimChunk, WorldSim},
8 util::{Grid, Sampler, seed_expan},
9};
10use common::{
11 calendar::Calendar,
12 generation::EntityInfo,
13 terrain::{
14 Block, BlockKind, SpriteCfg, Structure, TerrainChunk, TerrainChunkSize,
15 structure::StructureBlock,
16 },
17 vol::{ReadVol, RectVolSize, WriteVol},
18};
19use rand::prelude::*;
20use rand_chacha::ChaChaRng;
21use std::{borrow::Cow, ops::Deref};
22use vek::*;
23
24#[derive(Copy, Clone)]
25pub struct CanvasInfo<'a> {
26 pub(crate) chunk_pos: Vec2<i32>,
27 pub(crate) wpos: Vec2<i32>,
28 pub(crate) column_grid: &'a Grid<Option<ZCache<'a>>>,
29 pub(crate) column_grid_border: i32,
30 pub(crate) chunks: &'a WorldSim,
31 pub(crate) index: IndexRef<'a>,
32 pub(crate) chunk: &'a SimChunk,
33 pub(crate) calendar: Option<&'a Calendar>,
34}
35
36impl<'a> CanvasInfo<'a> {
37 pub fn calendar(&self) -> Option<&'a Calendar> { self.calendar }
38
39 pub fn wpos(&self) -> Vec2<i32> { self.wpos }
40
41 pub fn area(&self) -> Aabr<i32> {
42 Rect::from((
43 self.wpos(),
44 Extent2::from(TerrainChunkSize::RECT_SIZE.map(|e| e as i32)),
45 ))
46 .into()
47 }
48
49 pub fn col(&self, wpos: Vec2<i32>) -> Option<&'a ColumnSample> {
50 self.column_grid
51 .get(self.column_grid_border + wpos - self.wpos())
52 .and_then(Option::as_ref)
53 .map(|zc| &zc.sample)
54 }
55
56 pub fn col_or_gen(&self, wpos: Vec2<i32>) -> Option<Cow<'a, ColumnSample>> {
61 self.col(wpos).map(Cow::Borrowed).or_else(|| {
62 Some(Cow::Owned(ColumnGen::new(self.chunks()).get((
63 wpos,
64 self.index(),
65 self.calendar,
66 ))?))
67 })
68 }
69
70 pub fn nearby_spots(&self) -> impl Iterator<Item = (Vec2<i32>, Spot, u32)> + '_ {
73 (-1..2)
74 .flat_map(|x| (-1..2).map(move |y| Vec2::new(x, y)))
75 .filter_map(move |pos| {
76 let pos = self.chunk_pos + pos;
77 self.chunks.get(pos).and_then(|c| c.spot).map(|spot| {
78 let wpos = pos.map2(TerrainChunkSize::RECT_SIZE, |e, sz| {
79 e * sz as i32 + sz as i32 / 2
80 });
81 let seed = pos.x as u32 | (pos.y as u32).wrapping_shl(16);
83
84 (wpos, spot, seed ^ 0xA801D82E)
85 })
86 })
87 }
88
89 pub fn index(&self) -> IndexRef<'a> { self.index }
90
91 pub fn chunk(&self) -> &'a SimChunk { self.chunk }
92
93 pub fn chunks(&self) -> &'a WorldSim { self.chunks }
94
95 pub fn land(&self) -> Land<'_> { Land::from_sim(self.chunks) }
96
97 pub fn with_mock_canvas_info<A, F: for<'b> FnOnce(&CanvasInfo<'b>) -> A>(
98 index: IndexRef<'a>,
99 sim: &'a WorldSim,
100 f: F,
101 ) -> A {
102 let zcache_grid = Grid::populate_from(Vec2::broadcast(1), |_| None);
103 let sim_chunk = SimChunk {
104 chaos: 0.0,
105 alt: 0.0,
106 basement: 0.0,
107 water_alt: 0.0,
108 downhill: None,
109 flux: 0.0,
110 temp: 0.0,
111 humidity: 0.0,
112 rockiness: 0.0,
113 tree_density: 0.0,
114 forest_kind: crate::all::ForestKind::Palm,
115 spawn_rate: 0.0,
116 river: Default::default(),
117 surface_veg: 0.0,
118 sites: Vec::new(),
119 place: None,
120 poi: None,
121 path: Default::default(),
122 cliff_height: 0.0,
123 contains_waypoint: false,
124 spot: None,
125 };
126 f(&CanvasInfo {
127 chunk_pos: Vec2::zero(),
128 wpos: Vec2::zero(),
129 column_grid: &zcache_grid,
130 column_grid_border: 0,
131 chunks: sim,
132 index,
133 chunk: &sim_chunk,
134 calendar: None,
135 })
136 }
137}
138
139pub struct Canvas<'a> {
140 pub(crate) info: CanvasInfo<'a>,
141 pub(crate) chunk: &'a mut TerrainChunk,
142 pub(crate) entities: Vec<EntityInfo>,
143 pub(crate) rtsim_resource_blocks: Vec<Vec3<i32>>,
144}
145
146impl<'a> Canvas<'a> {
147 pub fn info(&mut self) -> CanvasInfo<'a> { self.info }
152
153 pub fn get(&self, pos: Vec3<i32>) -> Block {
154 self.chunk
155 .get(pos - self.wpos())
156 .ok()
157 .copied()
158 .unwrap_or_else(Block::empty)
159 }
160
161 pub fn set(&mut self, pos: Vec3<i32>, block: Block) {
165 if block.get_rtsim_resource().is_some() {
166 self.rtsim_resource_blocks.push(pos);
167 }
168 let _ = self.chunk.set(pos - self.wpos(), block);
169 }
170
171 pub fn map(&mut self, pos: Vec3<i32>, f: impl FnOnce(Block) -> Block) {
175 let _ = self.chunk.map(pos - self.wpos(), f);
176 }
177
178 pub fn map_resource(&mut self, pos: Vec3<i32>, f: impl FnOnce(Block) -> Block) {
181 let _ = self.chunk.map(pos - self.wpos(), |b| {
182 let new_block = f(b);
183 if new_block.get_rtsim_resource().is_some() {
184 self.rtsim_resource_blocks.push(pos);
185 }
186 new_block
187 });
188 }
189
190 pub fn set_sprite_cfg(&mut self, pos: Vec3<i32>, sprite_cfg: SpriteCfg) {
191 let rpos = pos - self.wpos();
192 self.chunk.meta_mut().set_sprite_cfg_at(rpos, sprite_cfg);
193 }
194
195 pub fn foreach_col_area(
196 &mut self,
197 aabr: Aabr<i32>,
198 mut f: impl FnMut(&mut Self, Vec2<i32>, &ColumnSample),
199 ) {
200 let chunk_aabr = Aabr {
201 min: self.wpos(),
202 max: self.wpos() + Vec2::from(self.area().size()),
203 };
204
205 for y in chunk_aabr.min.y.max(aabr.min.y)..chunk_aabr.max.y.min(aabr.max.y) {
206 for x in chunk_aabr.min.x.max(aabr.min.x)..chunk_aabr.max.x.min(aabr.max.x) {
207 let wpos2d = Vec2::new(x, y);
208 let info = self.info;
209 let col = if let Some(col) = info.col(wpos2d) {
210 col
211 } else {
212 return;
213 };
214 f(self, wpos2d, col);
215 }
216 }
217 }
218
219 pub fn foreach_col(&mut self, f: impl FnMut(&mut Self, Vec2<i32>, &ColumnSample)) {
221 self.foreach_col_area(
222 Aabr {
223 min: Vec2::broadcast(i32::MIN),
224 max: Vec2::broadcast(i32::MAX),
225 },
226 f,
227 );
228 }
229
230 pub fn blit_structure(
236 &mut self,
237 origin: Vec3<i32>,
238 structure: &Structure,
239 seed: u32,
240 units: Vec2<Vec2<i32>>,
241 with_snow: bool,
242 ) {
243 let mut entities: Vec<(Vec3<f32>, String)> = Vec::new();
244 let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(seed));
245 let info = self.info();
246 self.foreach_col(|canvas, wpos2d, col| {
247 let rpos2d = wpos2d - origin.xy();
248 let rpos2d = units.x * rpos2d.x + units.y * rpos2d.y;
249
250 let mut above = true;
251 for z in (structure.get_bounds().min.z..structure.get_bounds().max.z).rev() {
252 if let Ok(sblock) = structure.get(rpos2d.with_z(z)) {
253 let mut add_snow = false;
254 let mut new_sprite_cfg = None;
255 let wpos = wpos2d.with_z(origin.z + z);
256 canvas.map(wpos, |block| {
257 if let Some((new_block, sprite_cfg)) = block_from_structure(
258 info.index,
259 sblock,
260 wpos2d.with_z(origin.z + z),
261 origin.xy(),
262 seed,
263 col,
264 |sprite| block.with_sprite(sprite),
265 info.calendar,
266 &units,
267 ) {
268 new_sprite_cfg = sprite_cfg;
269 if !new_block.is_air() {
270 if with_snow && col.snow_cover && above {
271 add_snow = true;
272 }
273 above = false;
274 }
275 new_block
276 } else {
277 block
278 }
279 });
280
281 if let Some(sprite_cfg) = new_sprite_cfg {
283 canvas.set_sprite_cfg(wpos, sprite_cfg);
284 }
285
286 if let StructureBlock::EntitySpawner(spec, spawn_chance) = sblock {
288 if rng.gen::<f32>() < *spawn_chance {
289 entities.push((
290 wpos2d.with_z(origin.z + z).map(|e| e as f32)
291 + Vec3::new(0.5, 0.5, 0.0),
292 spec.clone(),
293 ));
294 }
295 }
296
297 if add_snow {
298 canvas.set(
299 wpos2d.with_z(origin.z + z + 1),
300 Block::new(BlockKind::Snow, Rgb::new(210, 210, 255)),
301 );
302 }
303 }
304 }
305 });
306 for (pos, spec) in entities.drain(..) {
307 self.spawn(EntityInfo::at(pos).with_asset_expect(&spec, &mut rng, None));
308 }
309 }
310
311 pub fn find_spawn_pos(&self, wpos: Vec3<i32>) -> Option<Vec3<i32>> {
312 let height = 2;
313 let search_dist: i32 = 8;
314
315 (1..search_dist * 2 + 1)
316 .rev()
317 .map(|z| wpos.z + if z % 2 != 0 { z / 2 } else { -(z / 2) })
318 .find(|&z| {
319 self.get(wpos.xy().with_z(z - 1)).is_solid()
320 && (0..height).all(|z_offs| self.get(wpos.xy().with_z(z + z_offs)).is_fluid())
321 })
322 .map(|z| wpos.xy().with_z(z))
323 }
324
325 pub fn spawn(&mut self, entity: EntityInfo) { self.entities.push(entity); }
326}
327
328impl<'a> Deref for Canvas<'a> {
329 type Target = CanvasInfo<'a>;
330
331 fn deref(&self) -> &Self::Target { &self.info }
332}