1use super::*;
2use crate::{
3 Land,
4 util::{CARDINALS, RandomField, Sampler},
5};
6use common::{
7 generation::SpecialEntity,
8 terrain::{BlockKind, SpriteKind},
9};
10use rand::prelude::*;
11use std::sync::Arc;
12use vek::*;
13
14pub struct CoastalWorkshop {
16 pub door_tile: Vec2<i32>,
18 bounds: Aabr<i32>,
20 pub(crate) alt: i32,
22}
23
24impl CoastalWorkshop {
25 pub fn generate(
26 land: &Land,
27 _rng: &mut impl Rng,
28 site: &Site,
29 door_tile: Vec2<i32>,
30 door_dir: Vec2<i32>,
31 tile_aabr: Aabr<i32>,
32 alt: Option<i32>,
33 ) -> Self {
34 let door_tile_pos = site.tile_center_wpos(door_tile);
35 let bounds = Aabr {
36 min: site.tile_wpos(tile_aabr.min),
37 max: site.tile_wpos(tile_aabr.max),
38 };
39 Self {
40 door_tile: door_tile_pos,
41 bounds,
42 alt: alt.unwrap_or_else(|| {
43 land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32
44 }) + 2,
45 }
46 }
47}
48
49impl Structure for CoastalWorkshop {
50 #[cfg(feature = "dyn-lib")]
51 #[unsafe(export_name = "as_dyn_structure_coastalworkshop")]
52 fn as_dyn_outer(&self) -> Option<(&dyn Structure, &'static str)> {
53 Some((Self::as_dyn_impl(self), "as_dyn_structure_coastalworkshop"))
54 }
55
56 fn spawn_rules_inner(
57 &self,
58 spawn_rules: &mut SpawnRules,
59 _land: &Land,
60 _wpos: Vec2<i32>,
61 weight: f32,
62 ) {
63 spawn_rules.prefer_alt(self.alt as f32, weight + weight);
64 }
65
66 fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
67 let base = self.alt + 1;
68 let center = self.bounds.center();
69 let white = Fill::Sampling(Arc::new(|center| {
70 Some(match (RandomField::new(0).get(center)) % 37 {
71 0..=8 => Block::new(BlockKind::Rock, Rgb::new(251, 251, 227)),
72 9..=17 => Block::new(BlockKind::Rock, Rgb::new(245, 245, 229)),
73 18..=26 => Block::new(BlockKind::Rock, Rgb::new(250, 243, 221)),
74 27..=35 => Block::new(BlockKind::Rock, Rgb::new(240, 240, 230)),
75 _ => Block::new(BlockKind::Rock, Rgb::new(255, 244, 193)),
76 })
77 }));
78 let blue_broken = Fill::Sampling(Arc::new(|center| {
79 Some(match (RandomField::new(0).get(center)) % 20 {
80 0 => Block::new(BlockKind::Rock, Rgb::new(30, 187, 235)),
81 _ => Block::new(BlockKind::Rock, Rgb::new(11, 146, 187)),
82 })
83 }));
84 let length = (14 + RandomField::new(0).get(center.with_z(base)) % 3) as i32;
85 let width = (12 + RandomField::new(0).get((center - 1).with_z(base)) % 3) as i32;
86 let height = (12 + RandomField::new(0).get((center + 1).with_z(base)) % 4) as i32;
87
88 painter
90 .aabb(Aabb {
91 min: Vec2::new(center.x - length - 6, center.y - width - 6).with_z(base - 2),
92 max: Vec2::new(center.x + length + 7, center.y + width + 7).with_z(base - 1),
93 })
94 .fill(blue_broken.clone());
95
96 for dir in CARDINALS {
97 let frame_pos = Vec2::new(
98 center.x + dir.x * (length + 5),
99 center.y + dir.y * (width + 5),
100 );
101 painter
102 .line(center.with_z(base - 1), frame_pos.with_z(base - 1), 3.0)
103 .fill(blue_broken.clone());
104 }
105 painter
107 .aabb(Aabb {
108 min: Vec2::new(center.x - length - 6, center.y - width - 6).with_z(base - height),
109 max: Vec2::new(center.x + length + 7, center.y + width + 7).with_z(base - 2),
110 })
111 .fill(white.clone());
112 for f in 0..8 {
113 painter
114 .aabb(Aabb {
115 min: Vec2::new(center.x - length - 7 - f, center.y - width - 7 - f)
116 .with_z(base - 3 - f),
117 max: Vec2::new(center.x + length + 8 + f, center.y + width + 8 + f)
118 .with_z(base - 2 - f),
119 })
120 .fill(white.clone());
121 }
122 painter
124 .aabb(Aabb {
125 min: Vec2::new(center.x - length - 5, center.y - width - 5).with_z(base - 2),
126 max: Vec2::new(center.x + length + 6, center.y + width + 6).with_z(base + height),
127 })
128 .clear();
129 for dir in CARDINALS {
131 let clear_pos = Vec2::new(
132 center.x + dir.x * (length + 7),
133 center.y + dir.y * (width + 7),
134 );
135 painter
136 .line(center.with_z(base - 1), clear_pos.with_z(base - 1), 2.0)
137 .clear();
138 }
139 painter
141 .aabb(Aabb {
142 min: Vec2::new(center.x - length - 3, center.y - width - 3)
143 .with_z(base - 3 + height),
144 max: Vec2::new(center.x + length + 2, center.y + width + 2)
145 .with_z(base - 2 + height),
146 })
147 .fill(white.clone());
148 painter
149 .aabb(Aabb {
150 min: Vec2::new(center.x - length - 3, center.y - width - 3)
151 .with_z(base - 2 + height),
152 max: Vec2::new(center.x + length + 2, center.y + width + 2)
153 .with_z(base - 1 + height),
154 })
155 .fill(blue_broken.clone());
156 painter
157 .aabb(Aabb {
158 min: Vec2::new(center.x - length - 2, center.y - width - 2)
159 .with_z(base - 2 + height),
160 max: Vec2::new(center.x + length + 1, center.y + width + 1)
161 .with_z(base - 1 + height),
162 })
163 .clear();
164 painter
166 .aabb(Aabb {
167 min: Vec2::new(center.x - length, center.y - width).with_z(base - 2),
168 max: Vec2::new(center.x + length, center.y + width).with_z(base - 1),
169 })
170 .fill(blue_broken.clone());
171 painter
172 .aabb(Aabb {
173 min: Vec2::new(center.x - length + 1, center.y - width + 1).with_z(base - 2),
174 max: Vec2::new(center.x + length - 1, center.y + width - 1)
175 .with_z(base - 1 + height - 1),
176 })
177 .fill(white.clone());
178
179 let entry_limit = painter.aabb(Aabb {
181 min: Vec2::new(center.x - length, center.y - width).with_z(base - 2),
182 max: Vec2::new(center.x + length, center.y + width).with_z(base - 1 + height - 1),
183 });
184 painter
185 .line(
186 Vec2::new(center.x, center.y + 1 - width).with_z(base - 1),
187 Vec2::new(center.x, center.y - 2 + width).with_z(base - 1),
188 8.0,
189 )
190 .intersect(entry_limit)
191 .fill(blue_broken.clone());
192 painter
193 .line(
194 Vec2::new(center.x, center.y - width).with_z(base - 1),
195 Vec2::new(center.x, center.y + width).with_z(base - 1),
196 7.0,
197 )
198 .intersect(entry_limit)
199 .clear();
200 painter
201 .line(
202 Vec2::new(center.x + 1 - length, center.y).with_z(base - 1),
203 Vec2::new(center.x - 2 + length, center.y).with_z(base - 1),
204 8.0,
205 )
206 .intersect(entry_limit)
207 .fill(blue_broken.clone());
208 painter
209 .line(
210 Vec2::new(center.x - length, center.y).with_z(base - 1),
211 Vec2::new(center.x + length, center.y).with_z(base - 1),
212 7.0,
213 )
214 .intersect(entry_limit)
215 .clear();
216 painter
218 .aabb(Aabb {
219 min: Vec2::new(center.x - length + 2, center.y - width + 2).with_z(base - 2),
220 max: Vec2::new(center.x + length - 2, center.y + width - 2)
221 .with_z(base - 2 + height - 1),
222 })
223 .clear();
224
225 painter
227 .aabb(Aabb {
228 min: Vec2::new(center.x - length + 5, center.y - width + 5).with_z(base - 3),
229 max: Vec2::new(center.x + length - 5, center.y + width - 5).with_z(base - 2),
230 })
231 .fill(blue_broken.clone());
232 painter
233 .aabb(Aabb {
234 min: Vec2::new(center.x - length + 6, center.y - width + 6).with_z(base - 3),
235 max: Vec2::new(center.x + length - 6, center.y + width - 6).with_z(base - 2),
236 })
237 .fill(white.clone());
238
239 for d in 0..2 {
241 let door_lamp_pos =
242 Vec2::new(center.x - length + 2 + (d * ((2 * (length)) - 5)), center.y)
243 .with_z(base + 6);
244 painter.rotated_sprite(
245 door_lamp_pos,
246 SpriteKind::WallLampSmall,
247 2 + ((d * 4) as u8),
248 );
249
250 let lamp_pos = Vec2::new(center.x, center.y - width + 2 + (d * ((2 * (width)) - 5)))
251 .with_z(base + 6);
252 painter.rotated_sprite(lamp_pos, SpriteKind::WallLampSmall, 4 - ((d * 4) as u8));
253 }
254 for d in 0..2 {
255 let door_lamp_pos =
256 Vec2::new(center.x - length - 1 + (d * ((2 * (length)) + 1)), center.y)
257 .with_z(base + 6);
258 painter.rotated_sprite(
259 door_lamp_pos,
260 SpriteKind::WallLampSmall,
261 (6 + ((d * 4) as u8)) % 8,
262 );
263
264 let lamp_pos = Vec2::new(center.x, center.y - width - 1 + (d * ((2 * (width)) + 1)))
265 .with_z(base + 6);
266 painter.rotated_sprite(
267 lamp_pos,
268 SpriteKind::WallLampSmall,
269 (8 - ((d * 4) as u8)) % 8,
270 );
271 }
272
273 painter
275 .cylinder(Aabb {
276 min: (center - 4).with_z(base + height - 4),
277 max: (center + 2).with_z(base - 2 + height + (height / 2)),
278 })
279 .fill(blue_broken);
280
281 let top_limit = painter.aabb(Aabb {
282 min: Vec2::new(center.x - length, center.y - width).with_z(base + height - 2),
283 max: Vec2::new(center.x + length, center.y + width)
284 .with_z(base - 2 + height + (height / 2)),
285 });
286 painter
287 .superquadric(
288 Aabb {
289 min: Vec2::new(center.x - length - 1, center.y - width - 1)
290 .with_z(base + height - (height / 2)),
291 max: Vec2::new(center.x + length, center.y + width)
292 .with_z(base - 2 + height + (height / 2)),
293 },
294 1.5,
295 )
296 .intersect(top_limit)
297 .fill(white.clone());
298 painter
300 .cylinder(Aabb {
301 min: (center - 3).with_z(base + height - 4),
302 max: (center + 1).with_z(base - 2 + height + (height / 2)),
303 })
304 .clear();
305
306 painter
307 .cylinder(Aabb {
308 min: (center - 3).with_z(base - 2),
309 max: (center + 1).with_z(base - 1),
310 })
311 .fill(white);
312 painter
313 .aabb(Aabb {
314 min: (center - 2).with_z(base - 2),
315 max: (center).with_z(base - 1),
316 })
317 .clear();
318 painter
319 .aabb(Aabb {
320 min: (center - 2).with_z(base - 3),
321 max: (center).with_z(base - 2),
322 })
323 .fill(Fill::Block(Block::air(SpriteKind::Ember)));
324
325 let mut stations = vec![
326 SpriteKind::CraftingBench,
327 SpriteKind::Forge,
328 SpriteKind::SpinningWheel,
329 SpriteKind::TanningRack,
330 SpriteKind::CookingPot,
331 SpriteKind::Cauldron,
332 SpriteKind::Loom,
333 SpriteKind::Anvil,
334 SpriteKind::DismantlingBench,
335 SpriteKind::RepairBench,
336 ];
337 'outer: for d in 0..3 {
338 for dir in CARDINALS {
339 if stations.is_empty() {
340 break 'outer;
341 }
342 let position = center + dir * (4 + d * 2);
343 let cr_station = stations.swap_remove(
344 RandomField::new(0).get(position.with_z(base)) as usize % stations.len(),
345 );
346 painter.sprite(position.with_z(base - 2), cr_station);
347 }
348 }
349
350 painter.spawn(
351 EntityInfo::at((center - 2).with_z(base - 2).map(|e| e as f32 + 0.5))
352 .into_special(SpecialEntity::Waypoint),
353 );
354 }
355}