Skip to main content

veloren_world/site/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        alt: Option<i32>,
30    ) -> Self {
31        let bounds = Aabr {
32            min: site.tile_wpos(tile_aabr.min),
33            max: site.tile_wpos(tile_aabr.max),
34        };
35
36        Self {
37            bounds,
38            alt: alt.unwrap_or_else(|| {
39                land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32
40            }),
41        }
42    }
43}
44
45impl Structure for Workshop {
46    #[cfg(feature = "dyn-lib")]
47    #[unsafe(export_name = "as_dyn_structure_workshop")]
48    fn as_dyn_outer(&self) -> Option<(&dyn Structure, &'static str)> {
49        Some((Self::as_dyn_impl(self), "as_dyn_structure_workshop"))
50    }
51
52    fn spawn_rules_inner(
53        &self,
54        spawn_rules: &mut SpawnRules,
55        _land: &Land,
56        _wpos: Vec2<i32>,
57        weight: f32,
58    ) {
59        spawn_rules.prefer_alt(self.alt as f32, weight);
60    }
61
62    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
63        let brick = Fill::Brick(BlockKind::Rock, Rgb::new(80, 75, 85), 24);
64
65        let base = self.alt + 1;
66        let center = self.bounds.center();
67
68        // Base
69        painter
70            .aabb(Aabb {
71                min: (self.bounds.min + 1).with_z(base - 16),
72                max: self.bounds.max.with_z(base),
73            })
74            .fill(brick.clone());
75
76        let roof = base + 5;
77
78        painter
79            .aabb(Aabb {
80                min: (self.bounds.min + 2).with_z(base),
81                max: (self.bounds.max - 1).with_z(roof),
82            })
83            .clear();
84
85        // Supports
86        for pos in [
87            Vec2::new(self.bounds.min.x + 3, self.bounds.min.y + 3),
88            Vec2::new(self.bounds.max.x - 3, self.bounds.min.y + 3),
89            Vec2::new(self.bounds.min.x + 3, self.bounds.max.y - 3),
90            Vec2::new(self.bounds.max.x - 3, self.bounds.max.y - 3),
91        ] {
92            painter
93                .line(pos.with_z(base), pos.with_z(roof), 1.0)
94                .fill(Fill::Block(Block::new(
95                    BlockKind::Wood,
96                    Rgb::new(55, 25, 8),
97                )));
98        }
99
100        let roof_top = roof + 5;
101
102        // Roof
103        painter
104            .pyramid(Aabb {
105                min: (self.bounds.min + 2).with_z(roof),
106                max: (self.bounds.max - 1).with_z(roof_top),
107            })
108            .fill(Fill::Brick(BlockKind::Rock, Rgb::new(45, 28, 21), 24));
109
110        let chimney = roof_top + 2;
111
112        // Chimney
113        let chimney_radius = 3.0;
114        painter
115            .line(
116                center.with_z(base + 4),
117                center.with_z(chimney),
118                chimney_radius,
119            )
120            .fill(brick);
121        painter
122            .line(
123                center.with_z(base),
124                center.with_z(chimney + 2),
125                chimney_radius - 1.0,
126            )
127            .clear();
128        for x in -1..2 {
129            for y in -1..2 {
130                painter.sprite(
131                    (center + Vec2::new(x, y)).with_z(base - 1),
132                    SpriteKind::Ember,
133                );
134            }
135        }
136        let mut stations = vec![
137            SpriteKind::CraftingBench,
138            SpriteKind::Forge,
139            SpriteKind::SpinningWheel,
140            SpriteKind::TanningRack,
141            SpriteKind::CookingPot,
142            SpriteKind::Cauldron,
143            SpriteKind::Loom,
144            SpriteKind::Anvil,
145            SpriteKind::DismantlingBench,
146            SpriteKind::RepairBench,
147        ];
148        'outer: for d in 0..3 {
149            for dir in CARDINALS {
150                if stations.is_empty() {
151                    break 'outer;
152                }
153                let position = center + dir * (3 + d * 2);
154                let cr_station = stations.swap_remove(
155                    RandomField::new(0).get(position.with_z(base)) as usize % stations.len(),
156                );
157                painter.sprite(position.with_z(base), cr_station);
158            }
159        }
160
161        painter.spawn(
162            EntityInfo::at(self.bounds.center().with_z(base).map(|e| e as f32 + 0.5))
163                .into_special(SpecialEntity::Waypoint),
164        );
165    }
166}