veloren_world/site2/plot/
terracotta_yard.rs

1use super::*;
2use crate::{
3    Land,
4    assets::AssetHandle,
5    site2::gen::PrimitiveTransform,
6    util::{NEIGHBORS, RandomField, Sampler},
7};
8use common::{
9    generation::EntityInfo,
10    terrain::{BlockKind, SpriteKind, Structure as PrefabStructure, StructuresGroup},
11};
12use lazy_static::lazy_static;
13use rand::prelude::*;
14use std::{f32::consts::TAU, sync::Arc};
15use vek::*;
16
17/// Represents house data generated by the `generate()` method
18pub struct TerracottaYard {
19    /// Axis aligned bounding region for the house
20    bounds: Aabr<i32>,
21    /// Approximate altitude of the door tile
22    pub(crate) alt: i32,
23}
24
25impl TerracottaYard {
26    pub fn generate(
27        land: &Land,
28        _rng: &mut impl Rng,
29        site: &Site,
30        tile_aabr: Aabr<i32>,
31        alt: Option<i32>,
32    ) -> Self {
33        let bounds = Aabr {
34            min: site.tile_wpos(tile_aabr.min),
35            max: site.tile_wpos(tile_aabr.max),
36        };
37        Self {
38            bounds,
39            alt: alt.unwrap_or_else(|| {
40                land.get_alt_approx(site.tile_center_wpos((tile_aabr.max - tile_aabr.min) / 2))
41                    as i32
42                    + 2
43            }),
44        }
45    }
46
47    pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
48        SpawnRules {
49            waypoints: false,
50            trees: wpos.distance_squared(self.bounds.center()) > (85_i32).pow(2),
51            ..SpawnRules::default()
52        }
53    }
54}
55
56impl Structure for TerracottaYard {
57    #[cfg(feature = "use-dyn-lib")]
58    const UPDATE_FN: &'static [u8] = b"render_terracotta_yard\0";
59
60    #[cfg_attr(feature = "be-dyn-lib", export_name = "render_terracotta_yard")]
61    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
62        let base = self.alt + 4;
63        let center = self.bounds.center();
64        let mut rng = thread_rng();
65        let clay_unbroken = Fill::Sampling(Arc::new(|center| {
66            Some(match (RandomField::new(0).get(center)) % 40 {
67                0..=8 => Block::new(BlockKind::Rock, Rgb::new(242, 161, 53)),
68                9..=17 => Block::new(BlockKind::Rock, Rgb::new(253, 199, 81)),
69                18..=26 => Block::new(BlockKind::Rock, Rgb::new(254, 210, 91)),
70                27..=35 => Block::new(BlockKind::Rock, Rgb::new(254, 216, 101)),
71                _ => Block::new(BlockKind::Rock, Rgb::new(250, 185, 71)),
72            })
73        }));
74        let clay_broken = Fill::Sampling(Arc::new(|center| {
75            Some(match (RandomField::new(0).get(center)) % 42 {
76                0..=8 => Block::new(BlockKind::Rock, Rgb::new(242, 161, 53)),
77                9..=17 => Block::new(BlockKind::Rock, Rgb::new(253, 199, 81)),
78                18..=26 => Block::new(BlockKind::Rock, Rgb::new(254, 210, 91)),
79                27..=35 => Block::new(BlockKind::Rock, Rgb::new(254, 216, 101)),
80                36..=38 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
81                _ => Block::new(BlockKind::Rock, Rgb::new(250, 185, 71)),
82            })
83        }));
84        let grass_fill = Fill::Sampling(Arc::new(|wpos| {
85            Some(match (RandomField::new(0).get(wpos)) % 20 {
86                1..=2 => Block::air(SpriteKind::JungleRedGrass),
87                3..=7 => Block::air(SpriteKind::JungleLeafyPlant),
88                8 => Block::air(SpriteKind::JungleFern),
89                _ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
90            })
91        }));
92        let water_fill = Fill::Block(Block::new(BlockKind::Water, Rgb::zero()));
93        let sand = Fill::Brick(BlockKind::Misc, Rgb::new(235, 178, 99), 12);
94        let size = 30;
95        let room_size = 15 * (size / 10);
96        let water = RandomField::new(0).get(center.with_z(base)) % 2;
97        // foundation
98        painter
99            .superquadric(
100                Aabb {
101                    min: (center - (room_size / 2) - 10).with_z(base - room_size - 15),
102                    max: (center + (room_size / 2) + 10).with_z(base + 5),
103                },
104                2.5,
105            )
106            .fill(clay_unbroken.clone());
107        // clear floor
108        let clear_limit = painter.aabb(Aabb {
109            min: (center - (room_size / 2) - 5).with_z(base),
110            max: (center + (room_size / 2) + 5).with_z(base + room_size),
111        });
112        painter
113            .superquadric(
114                Aabb {
115                    min: (center - (room_size / 2) - 5).with_z(base - (room_size / 2) - 5),
116                    max: (center + (room_size / 2) + 5).with_z(base + room_size),
117                },
118                2.5,
119            )
120            .intersect(clear_limit)
121            .clear();
122        if water < 1 {
123            // iron spike trap
124            painter
125                .cylinder(Aabb {
126                    min: (center - (room_size / 4)).with_z(base - 12),
127                    max: (center + (room_size / 4)).with_z(base),
128                })
129                .clear();
130            painter
131                .cylinder(Aabb {
132                    min: (center - (room_size / 4)).with_z(base - 13),
133                    max: (center + (room_size / 4)).with_z(base - 12),
134                })
135                .fill(Fill::Block(Block::air(SpriteKind::IronSpike)));
136            painter
137                .cylinder(Aabb {
138                    min: (center - (room_size / 4)).with_z(base - 1),
139                    max: (center + (room_size / 4)).with_z(base),
140                })
141                .fill(Fill::Block(Block::air(SpriteKind::TerracottaBlock)));
142            painter
143                .cylinder(Aabb {
144                    min: (center).with_z(base - 14),
145                    max: (center + 1).with_z(base),
146                })
147                .fill(clay_unbroken);
148            // sprites
149            let radius_lamps_main = (room_size / 4) + 6;
150            let lamps_main = 8.0_f32;
151            let phi_lamps_main = TAU / lamps_main;
152            for n in 1..=lamps_main as i32 {
153                let pos = Vec2::new(
154                    center.x
155                        + (radius_lamps_main as f32 * ((n as f32 * phi_lamps_main).cos())) as i32,
156                    center.y
157                        + (radius_lamps_main as f32 * ((n as f32 * phi_lamps_main).sin())) as i32,
158                );
159                painter.sprite(pos.with_z(base), SpriteKind::TerracottaStatue);
160            }
161            // npcs
162            // statue
163            painter.spawn(
164                EntityInfo::at((center).with_z(base).as_()).with_asset_expect(
165                    "common.entity.dungeon.terracotta.terracotta_statue_key",
166                    &mut rng,
167                    None,
168                ),
169            );
170        } else {
171            let basin = painter.superquadric(
172                Aabb {
173                    min: (center - (room_size / 4) - 12).with_z(base - 12),
174                    max: (center + (room_size / 4) + 12).with_z(base + 12),
175                },
176                2.5,
177            );
178            basin.clear();
179            let water_level = (RandomField::new(0).get(center.with_z(base)) % 8) as i32;
180            let water_limit = painter.aabb(Aabb {
181                min: (center - (room_size / 4) - 12).with_z(base - 12),
182                max: (center + (room_size / 4) + 12).with_z(base - 4 - water_level),
183            });
184            basin.intersect(water_limit).fill(water_fill);
185            // models
186            let model_radius = (room_size / 2) + 7;
187            for dir in NEIGHBORS {
188                let pos = center + dir * model_radius;
189                // foundation
190                painter
191                    .cylinder(Aabb {
192                        min: (pos - 10).with_z(base - room_size),
193                        max: (pos + 10).with_z(base - 3),
194                    })
195                    .fill(clay_unbroken.clone());
196                painter
197                    .cylinder(Aabb {
198                        min: (pos - 10).with_z(base - 4),
199                        max: (pos + 10).with_z(base - 3),
200                    })
201                    .fill(clay_broken.clone());
202                painter
203                    .cylinder(Aabb {
204                        min: (pos - 9).with_z(base - 4),
205                        max: (pos + 9).with_z(base - 3),
206                    })
207                    .fill(sand.clone());
208                // jungle sprites
209                painter
210                    .cylinder(Aabb {
211                        min: (pos - 7).with_z(base - 3),
212                        max: (pos + 7).with_z(base - 2),
213                    })
214                    .fill(grass_fill.clone());
215                // models
216                let model_pos = pos.with_z(base - 5);
217                match RandomField::new(0).get(model_pos) % 2 {
218                    0 => {
219                        lazy_static! {
220                            pub static ref MODEL: AssetHandle<StructuresGroup> =
221                                PrefabStructure::load_group(
222                                    "site_structures.terracotta.terracotta_decor_small"
223                                );
224                        }
225                        let rng = RandomField::new(0).get(model_pos) % 62;
226                        let model = MODEL.read();
227                        let model = model[rng as usize % model.len()].clone();
228                        painter
229                            .prim(Primitive::Prefab(Box::new(model.clone())))
230                            .translate(model_pos)
231                            .fill(Fill::Prefab(Box::new(model), model_pos, rng));
232                    },
233
234                    _ => {
235                        lazy_static! {
236                            pub static ref MODEL: AssetHandle<StructuresGroup> =
237                                PrefabStructure::load_group("trees.palms");
238                        }
239                        let rng = RandomField::new(0).get(model_pos) % 62;
240                        let model = MODEL.read();
241                        let model = model[rng as usize % model.len()].clone();
242                        painter
243                            .prim(Primitive::Prefab(Box::new(model.clone())))
244                            .translate(model_pos)
245                            .fill(Fill::Prefab(Box::new(model), model_pos, rng));
246                    },
247                }
248            }
249        }
250        // guards
251        let radius_guards = (room_size / 4) + 12;
252        let guards = 4.0_f32;
253        let phi_guards = TAU / guards;
254        for n in 1..=guards as i32 {
255            let pos = Vec2::new(
256                center.x + (radius_guards as f32 * ((n as f32 * phi_guards).cos())) as i32,
257                center.y + (radius_guards as f32 * ((n as f32 * phi_guards).sin())) as i32,
258            )
259            .with_z(base);
260            terracotta_palace::spawn_random_entity(pos, painter, 1..=1);
261        }
262    }
263}