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