veloren_world/site2/plot/
citadel.rs1use super::*;
2use crate::{Land, util::NEIGHBORS};
3use rand::prelude::*;
4use std::ops::{Add, Div, Mul};
5use vek::*;
6
7struct Cell {
8 alt: i32,
9 colonade: Option<i32>,
10}
11
12const CELL_SIZE: i32 = 16;
13
14pub struct Citadel {
15 name: String,
16 _seed: u32,
17 origin: Vec3<i32>,
18 radius: i32,
19 grid: Grid<Option<Cell>>,
20}
21
22impl Citadel {
23 pub fn generate(wpos: Vec2<i32>, land: &Land, rng: &mut impl Rng) -> Self {
24 let alt = land.get_alt_approx(wpos) as i32;
25
26 let name = NameGen::location(rng).generate_town();
27 let seed = rng.gen();
28 let origin = wpos.with_z(alt);
29
30 let radius = 150;
31
32 let cell_radius = radius / CELL_SIZE;
33 let mut grid = Grid::populate_from(Vec2::broadcast((cell_radius + 1) * 2), |pos| {
34 let rpos = pos - cell_radius;
35 if rpos.magnitude_squared() < cell_radius.pow(2) {
36 let height = Lerp::lerp(
37 120.0,
38 24.0,
39 rpos.map(i32::abs).reduce_max() as f32 / cell_radius as f32,
40 );
41 let level_height = 32.0;
42 Some(Cell {
43 alt: land
44 .get_alt_approx(wpos + rpos * CELL_SIZE + CELL_SIZE / 2)
45 .add(height)
46 .div(level_height)
47 .floor()
48 .mul(level_height) as i32,
49 colonade: None,
50 })
51 } else {
52 None
53 }
54 });
55
56 for y in 0..grid.size().y {
57 for x in 0..grid.size().x {
58 let pos = Vec2::new(x, y);
59 if let Some(min_alt) = NEIGHBORS
60 .into_iter()
61 .filter_map(|rpos| Some(grid.get(pos + rpos)?.as_ref()?.alt))
62 .min()
63 {
64 let Some(Some(cell)) = grid.get_mut(pos) else {
65 continue;
66 };
67 if min_alt < cell.alt {
68 cell.colonade = Some(min_alt);
69 }
70 }
71 }
72 }
73
74 Self {
75 name,
76 _seed: seed,
77 origin,
78 radius,
79 grid,
80 }
81 }
82
83 pub fn name(&self) -> &str { &self.name }
84
85 pub fn radius(&self) -> i32 { self.radius }
86
87 pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
88 SpawnRules {
89 trees: (wpos - self.origin).map(i32::abs).reduce_max() > self.radius,
90 waypoints: false,
91 ..SpawnRules::default()
92 }
93 }
94
95 fn wpos_cell(&self, wpos: Vec2<i32>) -> Vec2<i32> {
96 (wpos - self.origin) / CELL_SIZE + self.grid.size() / 2
97 }
98
99 fn cell_wpos(&self, pos: Vec2<i32>) -> Vec2<i32> {
100 (pos - self.grid.size() / 2) * CELL_SIZE + self.origin
101 }
102}
103
104impl Structure for Citadel {
105 #[cfg(feature = "use-dyn-lib")]
106 const UPDATE_FN: &'static [u8] = b"render_citadel\0";
107
108 #[cfg_attr(feature = "be-dyn-lib", export_name = "render_citadel")]
109 fn render_inner(&self, _site: &Site, land: &Land, painter: &Painter) {
110 for (pos, cell) in self.grid.iter_area(
111 self.wpos_cell(painter.render_aabr().min) - 1,
112 Vec2::<i32>::from(painter.render_aabr().size()) / CELL_SIZE + 2,
113 ) {
114 if let Some(cell) = cell {
115 let wpos = self.cell_wpos(pos);
116 painter
118 .aabb(Aabb {
119 min: wpos.with_z(cell.alt),
120 max: (wpos + CELL_SIZE).with_z(cell.alt + 16),
121 })
122 .clear();
123
124 let mut prim = painter.aabb(Aabb {
125 min: wpos.with_z(land.get_alt_approx(wpos + CELL_SIZE / 2) as i32 - 32),
126 max: (wpos + CELL_SIZE).with_z(cell.alt),
127 });
128
129 if let Some(colonade_alt) = cell.colonade {
131 let hole = painter
132 .aabb(Aabb {
133 min: wpos.with_z(colonade_alt),
134 max: (wpos + CELL_SIZE).with_z(cell.alt),
135 })
136 .intersect(painter.prim(Primitive::Superquadric {
137 aabb: Aabb {
138 min: (wpos - 1).with_z(colonade_alt - 32),
139 max: (wpos + 1 + CELL_SIZE).with_z(cell.alt - 1),
140 },
141 degree: 2.5,
142 }));
143 hole.clear();
144 prim = prim.without(hole);
145 }
146
147 for dir in CARDINALS {
149 if self
150 .grid
151 .get(pos + dir)
152 .and_then(Option::as_ref)
153 .is_none_or(|near| near.alt < cell.alt)
154 {
155 let offset = wpos + CELL_SIZE / 2 + dir * CELL_SIZE / 2;
156 let rad = dir.map(|e| if e == 0 { CELL_SIZE / 2 + 1 } else { 1 });
157 let height = if pos.sum() % 2 == 0 { 5 } else { 2 };
158 prim = prim.union(painter.aabb(Aabb {
159 min: (offset - rad).with_z(cell.alt - 6),
160 max: (offset + rad).with_z(cell.alt + height),
161 }));
162 }
163 }
164
165 prim.fill(Fill::Brick(BlockKind::Rock, Rgb::new(100, 100, 100), 20));
166 }
167 }
168 }
169}