veloren_world/site/plot/
workshop.rs1use 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
13pub struct Workshop {
15 bounds: Aabr<i32>,
17 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 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 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 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 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}