Skip to main content

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