veloren_world/site2/plot/
workshop.rs

1use super::*;
2use crate::{
3    Land,
4    util::{RandomField, Sampler},
5};
6use common::{
7    generation::{EntityInfo, SpecialEntity},
8    terrain::{Block, BlockKind, SpriteKind},
9};
10use rand::prelude::*;
11use vek::*;
12
13/// Represents house data generated by the `generate()` method
14pub struct Workshop {
15    /// Axis aligned bounding region for the house
16    bounds: Aabr<i32>,
17    /// Approximate altitude of the door tile
18    pub(crate) alt: i32,
19}
20
21impl Workshop {
22    pub fn generate(
23        land: &Land,
24        _rng: &mut impl Rng,
25        site: &Site,
26        door_tile: Vec2<i32>,
27        door_dir: Vec2<i32>,
28        tile_aabr: Aabr<i32>,
29    ) -> Self {
30        let bounds = Aabr {
31            min: site.tile_wpos(tile_aabr.min),
32            max: site.tile_wpos(tile_aabr.max),
33        };
34
35        Self {
36            bounds,
37            alt: land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32,
38        }
39    }
40}
41
42impl Structure for Workshop {
43    #[cfg(feature = "use-dyn-lib")]
44    const UPDATE_FN: &'static [u8] = b"render_workshop\0";
45
46    #[cfg_attr(feature = "be-dyn-lib", export_name = "render_workshop")]
47    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
48        let brick = Fill::Brick(BlockKind::Rock, Rgb::new(80, 75, 85), 24);
49
50        let base = self.alt + 1;
51        let center = self.bounds.center();
52
53        // Base
54        painter
55            .aabb(Aabb {
56                min: (self.bounds.min + 1).with_z(base - 16),
57                max: self.bounds.max.with_z(base),
58            })
59            .fill(brick.clone());
60
61        let roof = base + 5;
62
63        painter
64            .aabb(Aabb {
65                min: (self.bounds.min + 2).with_z(base),
66                max: (self.bounds.max - 1).with_z(roof),
67            })
68            .clear();
69
70        // Supports
71        for pos in [
72            Vec2::new(self.bounds.min.x + 3, self.bounds.min.y + 3),
73            Vec2::new(self.bounds.max.x - 3, self.bounds.min.y + 3),
74            Vec2::new(self.bounds.min.x + 3, self.bounds.max.y - 3),
75            Vec2::new(self.bounds.max.x - 3, self.bounds.max.y - 3),
76        ] {
77            painter
78                .line(pos.with_z(base), pos.with_z(roof), 1.0)
79                .fill(Fill::Block(Block::new(
80                    BlockKind::Wood,
81                    Rgb::new(55, 25, 8),
82                )));
83        }
84
85        let roof_top = roof + 5;
86
87        // Roof
88        painter
89            .pyramid(Aabb {
90                min: (self.bounds.min + 2).with_z(roof),
91                max: (self.bounds.max - 1).with_z(roof_top),
92            })
93            .fill(Fill::Brick(BlockKind::Rock, Rgb::new(45, 28, 21), 24));
94
95        let chimney = roof_top + 2;
96
97        // Chimney
98        let chimney_radius = 3.0;
99        painter
100            .line(
101                center.with_z(base + 4),
102                center.with_z(chimney),
103                chimney_radius,
104            )
105            .fill(brick);
106        painter
107            .line(
108                center.with_z(base),
109                center.with_z(chimney + 2),
110                chimney_radius - 1.0,
111            )
112            .clear();
113        for x in -1..2 {
114            for y in -1..2 {
115                painter.sprite(
116                    (center + Vec2::new(x, y)).with_z(base - 1),
117                    SpriteKind::Ember,
118                );
119            }
120        }
121        let mut stations = vec![
122            SpriteKind::CraftingBench,
123            SpriteKind::Forge,
124            SpriteKind::SpinningWheel,
125            SpriteKind::TanningRack,
126            SpriteKind::CookingPot,
127            SpriteKind::Cauldron,
128            SpriteKind::Loom,
129            SpriteKind::Anvil,
130            SpriteKind::DismantlingBench,
131            SpriteKind::RepairBench,
132        ];
133        'outer: for d in 0..3 {
134            for dir in CARDINALS {
135                if stations.is_empty() {
136                    break 'outer;
137                }
138                let position = center + dir * (3 + d * 2);
139                let cr_station = stations.swap_remove(
140                    RandomField::new(0).get(position.with_z(base)) as usize % stations.len(),
141                );
142                painter.sprite(position.with_z(base), cr_station);
143            }
144        }
145
146        painter.spawn(
147            EntityInfo::at(self.bounds.center().with_z(base).map(|e| e as f32 + 0.5))
148                .into_special(SpecialEntity::Waypoint),
149        );
150    }
151}