1use super::*;
2use crate::{Land, util::SQUARE_4};
3use common::terrain::{Block, BlockKind};
4use num::Integer;
5use rand::prelude::*;
6use vek::*;
7
8pub struct Castle {
9 tile_aabr: Aabr<i32>,
10 _bounds: Aabr<i32>,
11 gate_aabr: Aabr<i32>,
12 gate_alt: i32,
13 pub(crate) alt: i32,
14}
15
16impl Castle {
17 pub fn generate(
18 land: &Land,
19 _rng: &mut impl Rng,
20 site: &Site,
21 tile_aabr: Aabr<i32>,
22 gate_aabr: Aabr<i32>,
23 ) -> Self {
24 let alt = SQUARE_4
25 .iter()
26 .map(|corner| tile_aabr.min + (tile_aabr.max - tile_aabr.min - 1) * corner)
27 .map(|pos| land.get_alt_approx(site.tile_center_wpos(pos)) as i32)
28 .sum::<i32>()
29 / 4;
30
31 Self {
32 tile_aabr,
33 _bounds: Aabr {
34 min: site.tile_wpos(tile_aabr.min),
35 max: site.tile_wpos(tile_aabr.max),
36 },
37 gate_aabr,
38 gate_alt: land.get_alt_approx(site.tile_center_wpos(gate_aabr.center())) as i32,
39 alt,
40 }
41 }
42}
43
44impl Structure for Castle {
45 #[cfg(feature = "dyn-lib")]
46 #[unsafe(export_name = "as_dyn_structure_castle")]
47 fn as_dyn_outer(&self) -> Option<(&dyn Structure, &'static str)> {
48 Some((Self::as_dyn_impl(self), "as_dyn_structure_castle"))
49 }
50
51 fn render_inner(&self, site: &Site, _land: &Land, painter: &Painter) {
52 let wall_height = 24;
53 let parapet_height = 2;
54 let parapet_gap = 2;
55 let parapet_offset = 2;
56 let ts = TILE_SIZE as i32;
57 let tower_height = 16;
58 let keep_levels = 3;
59 let keep_level_height = 8;
60 let _keep_height = wall_height + keep_levels * keep_level_height + 1;
61 let wall_rgb = Rgb::new(38, 46, 43);
62 painter.fill(
64 painter.prim(Primitive::Aabb(Aabb {
65 min: site.tile_wpos(self.tile_aabr.min).with_z(self.gate_alt),
66 max: site
67 .tile_wpos(self.tile_aabr.max)
68 .with_z(self.alt + tower_height),
69 })),
70 Fill::Block(Block::empty()),
71 );
72 painter.fill(
73 painter.prim(Primitive::Aabb(Aabb {
74 min: site.tile_wpos(self.tile_aabr.min).with_z(self.gate_alt),
75 max: site.tile_wpos(self.tile_aabr.max).with_z(self.gate_alt + 1),
76 })),
77 Fill::Block(Block::new(BlockKind::Rock, Rgb::new(55, 45, 65))),
78 );
79 for x in 0..self.tile_aabr.size().w {
80 for y in 0..self.tile_aabr.size().h {
81 let tile_pos = self.tile_aabr.min + Vec2::new(x, y);
82 let wpos = site.tile_wpos(tile_pos);
83 match site.tiles.get(tile_pos).kind.clone() {
84 TileKind::Wall(ori) => {
85 let dir = ori.to_vec2();
86 let wall = painter.prim(Primitive::Aabb(Aabb {
87 min: wpos.with_z(self.alt - 20),
88 max: (wpos + ts).with_z(self.alt + wall_height),
89 }));
90 let parapet = painter.prim(Primitive::Aabb(Aabb {
93 min: (wpos - dir.yx()).with_z(self.alt + wall_height),
94 max: (wpos + ts * dir).with_z(self.alt + wall_height + parapet_height),
95 }));
96 let parapet2 = painter.prim(Primitive::Aabb(Aabb {
97 min: (wpos + ts * dir.yx()).with_z(self.alt + wall_height),
98 max: (wpos + (ts + 1) * dir.yx() + ts * dir)
99 .with_z(self.alt + wall_height + parapet_height),
100 }));
101 let cut_sides = painter.prim(Primitive::Aabb(Aabb {
102 min: (wpos + parapet_offset * dir - dir.yx())
103 .with_z(self.alt + wall_height + parapet_height - 1),
104 max: (wpos
105 + (ts + 1) * dir.yx()
106 + (parapet_offset + parapet_gap) * dir)
107 .with_z(self.alt + wall_height + parapet_height),
108 }));
109
110 painter.fill(wall, Fill::Brick(BlockKind::Rock, wall_rgb, 12));
111 let sides = painter.prim(Primitive::union(parapet, parapet2));
112 painter.fill(sides, Fill::Brick(BlockKind::Rock, wall_rgb, 12));
113 if (x + y).is_odd() {
114 painter.fill(
115 painter.prim(Primitive::Aabb(Aabb {
116 min: (wpos + 2 * dir - dir.yx()).with_z(self.alt - 20),
117 max: (wpos + 4 * dir + (ts + 1) * dir.yx())
118 .with_z(self.alt + wall_height),
119 })),
120 Fill::Brick(BlockKind::Rock, wall_rgb, 12),
121 );
122 } else {
123 let window_top = painter.prim(Primitive::Aabb(Aabb {
124 min: (wpos + 2 * dir).with_z(self.alt + wall_height / 4 + 9),
125 max: (wpos + (ts - 2) * dir + dir.yx())
126 .with_z(self.alt + wall_height / 4 + 12),
127 }));
128 let window_bottom = painter.prim(Primitive::Aabb(Aabb {
129 min: (wpos + 1 * dir).with_z(self.alt + wall_height / 4),
130 max: (wpos + (ts - 1) * dir + dir.yx())
131 .with_z(self.alt + wall_height / 4 + 9),
132 }));
133 let window_top2 = painter.prim(Primitive::Aabb(Aabb {
134 min: (wpos + 2 * dir + (ts - 1) * dir.yx())
135 .with_z(self.alt + wall_height / 4 + 9),
136 max: (wpos + (ts - 2) * dir + ts * dir.yx())
137 .with_z(self.alt + wall_height / 4 + 12),
138 }));
139 let window_bottom2 = painter.prim(Primitive::Aabb(Aabb {
140 min: (wpos + 1 * dir + (ts - 1) * dir.yx())
141 .with_z(self.alt + wall_height / 4),
142 max: (wpos + (ts - 1) * dir + ts * dir.yx())
143 .with_z(self.alt + wall_height / 4 + 9),
144 }));
145
146 painter.fill(window_bottom, Fill::Block(Block::empty()));
147 painter.fill(window_top, Fill::Block(Block::empty()));
148 painter.fill(window_bottom2, Fill::Block(Block::empty()));
149 painter.fill(window_top2, Fill::Block(Block::empty()));
150 }
151 painter.fill(cut_sides, Fill::Block(Block::empty()));
152 },
153 TileKind::Tower(roof) => {
154 let tower_total_height =
155 self.alt + wall_height + parapet_height + tower_height;
156 let tower_lower = painter.prim(Primitive::Aabb(Aabb {
157 min: (wpos - 1).with_z(self.alt - 20),
158 max: (wpos + ts + 1).with_z(tower_total_height),
159 }));
160 painter.fill(tower_lower, Fill::Brick(BlockKind::Rock, wall_rgb, 12));
161 let tower_upper = painter.prim(Primitive::Aabb(Aabb {
162 min: (wpos - 2).with_z(tower_total_height - 4i32),
163 max: (wpos + ts + 2).with_z(tower_total_height - 2i32),
164 }));
165 let tower_upper2 = painter.prim(Primitive::Aabb(Aabb {
166 min: (wpos - 3).with_z(tower_total_height - 2i32),
167 max: (wpos + ts + 3).with_z(tower_total_height),
168 }));
169
170 painter.fill(
171 painter.prim(Primitive::union(tower_upper, tower_upper2)),
172 Fill::Brick(BlockKind::Rock, wall_rgb, 12),
173 );
174
175 match roof {
176 RoofKind::Pyramid => {
177 let roof_lip = 1;
178 let roof_height = (ts + 3) / 2 + roof_lip + 1;
179
180 painter.fill(
181 painter.prim(Primitive::Pyramid {
182 aabb: Aabb {
183 min: (wpos - 3 - roof_lip).with_z(tower_total_height),
184 max: (wpos + ts + 3 + roof_lip)
185 .with_z(tower_total_height + roof_height),
186 },
187 inset: roof_height,
188 }),
189 Fill::Brick(BlockKind::Wood, Rgb::new(40, 5, 11), 10),
190 );
191 },
192 RoofKind::Parapet => {
193 let tower_top_outer = painter.prim(Primitive::Aabb(Aabb {
194 min: (wpos - 3).with_z(
195 self.alt + wall_height + parapet_height + tower_height,
196 ),
197 max: (wpos + ts + 3)
198 .with_z(tower_total_height + parapet_height),
199 }));
200 let tower_top_inner = painter.prim(Primitive::Aabb(Aabb {
201 min: (wpos - 2).with_z(tower_total_height),
202 max: (wpos + ts + 2)
203 .with_z(tower_total_height + parapet_height),
204 }));
205
206 tower_top_outer
207 .union(tower_top_inner)
208 .without(tower_top_outer.intersect(tower_top_inner))
209 .fill(Fill::Brick(BlockKind::Rock, wall_rgb, 12));
210
211 for x in (wpos.x..wpos.x + ts).step_by(2 * parapet_gap as usize) {
212 painter.fill(
213 painter.prim(Primitive::Aabb(Aabb {
214 min: Vec3::new(x, wpos.y - 3, tower_total_height + 1),
215 max: Vec3::new(
216 x + parapet_gap,
217 wpos.y + ts + 3,
218 tower_total_height + parapet_height,
219 ),
220 })),
221 Fill::Block(Block::empty()),
222 );
223 }
224 for y in (wpos.y..wpos.y + ts).step_by(2 * parapet_gap as usize) {
225 painter.fill(
226 painter.prim(Primitive::Aabb(Aabb {
227 min: Vec3::new(wpos.x - 3, y, tower_total_height + 1),
228 max: Vec3::new(
229 wpos.x + ts + 3,
230 y + parapet_gap,
231 tower_total_height + parapet_height,
232 ),
233 })),
234 Fill::Block(Block::empty()),
235 );
236 }
237
238 for &cpos in SQUARE_4.iter() {
239 let pos = wpos - 3 + (ts + 6) * cpos - cpos;
240 let pos2 = wpos - 2 + (ts + 4) * cpos - cpos;
241 painter.fill(
242 painter.prim(Primitive::Aabb(Aabb {
243 min: pos.with_z(tower_total_height - 2),
244 max: (pos + 1)
245 .with_z(tower_total_height + parapet_height),
246 })),
247 Fill::Block(Block::empty()),
248 );
249 painter.fill(
250 painter.prim(Primitive::Aabb(Aabb {
251 min: pos2.with_z(tower_total_height - 4),
252 max: (pos2 + 1).with_z(tower_total_height - 2),
253 })),
254 Fill::Block(Block::empty()),
255 );
256 }
257 },
258 }
259 },
260 TileKind::Keep(kind) => {
261 match kind {
262 KeepKind::Middle => {
263 for i in 0..keep_levels + 1 {
264 let height = keep_level_height * i;
265 painter.fill(
266 painter.prim(Primitive::Aabb(Aabb {
267 min: wpos.with_z(self.alt + height),
268 max: (wpos + ts).with_z(self.alt + height + 1),
269 })),
270 Fill::Block(Block::new(
271 BlockKind::Wood,
272 Rgb::new(89, 44, 14),
273 )),
274 );
275 }
276 },
277 KeepKind::Corner => {},
278 KeepKind::Wall(_ori) => {
279 for i in 0..keep_levels + 1 {
280 let _height = keep_level_height * i;
281 let _window_height = keep_level_height - 3;
283 }
284 },
285 }
286 },
287 _ => {},
288 }
289 }
290 }
291
292 let gate_aabb = Aabb {
295 min: (site.tile_wpos(self.gate_aabr.min) + Vec2::unit_x()).with_z(self.gate_alt - 1),
296 max: (site.tile_wpos(self.gate_aabr.max) - Vec2::unit_x())
297 .with_z(self.alt + wall_height),
298 };
299 painter.fill(
300 painter.prim(Primitive::Aabb(gate_aabb)),
301 Fill::Brick(BlockKind::Rock, wall_rgb, 12),
302 );
303 painter.fill(
304 painter.prim(Primitive::Aabb(Aabb {
305 min: (gate_aabb.min + Vec3::unit_x() * 2 + Vec3::unit_z() * 2),
306 max: (gate_aabb.max - Vec3::unit_x() * 2 - Vec3::unit_z() * 16),
307 })),
308 Fill::Block(Block::empty()),
309 );
310 let height = self.alt + wall_height - 17;
311 for i in 1..5 {
312 painter.fill(
313 painter.prim(Primitive::Aabb(Aabb {
314 min: Vec3::new(gate_aabb.min.x + 2 + i, gate_aabb.min.y, height + i),
315 max: Vec3::new(gate_aabb.max.x - 2 - i, gate_aabb.max.y, height + i + 1),
316 })),
317 Fill::Block(Block::empty()),
318 );
319 }
320 let height = self.alt + wall_height - 7;
321 for x in (gate_aabb.min.x + 1..gate_aabb.max.x - 2).step_by(4) {
322 painter.fill(
323 painter.prim(Primitive::Aabb(Aabb {
324 min: Vec3::new(x, gate_aabb.min.y + 1, height - 13),
325 max: Vec3::new(x + 2, gate_aabb.min.y + 2, height),
326 })),
327 Fill::Brick(BlockKind::Rock, Rgb::new(27, 35, 32), 8),
328 );
329 }
330 for z in (height - 12..height).step_by(4) {
331 painter.fill(
332 painter.prim(Primitive::Aabb(Aabb {
333 min: Vec3::new(gate_aabb.min.x + 2, gate_aabb.min.y + 1, z),
334 max: Vec3::new(gate_aabb.max.x - 2, gate_aabb.min.y + 2, z + 2),
335 })),
336 Fill::Brick(BlockKind::Rock, Rgb::new(27, 35, 32), 8),
337 );
338 }
339 }
340}