veloren_world/site2/plot/
castle.rs

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        // Flatten inside of the castle
61        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                        // TODO Figure out logic to choose on on which site wall should be placed
89                        // (inner, outer)
90                        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                                    // TODO clamp value in case of big heights
280                                    let _window_height = keep_level_height - 3;
281                                }
282                            },
283                        }
284                    },
285                    _ => {},
286                }
287            }
288        }
289
290        // Render gate here
291        // TODO move this into tile loop
292        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}