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