veloren_world/site2/plot/
terracotta_yard.rs1use 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
17pub struct TerracottaYard {
19 bounds: Aabr<i32>,
21 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 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 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 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 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 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 let model_radius = (room_size / 2) + 7;
187 for dir in NEIGHBORS {
188 let pos = center + dir * model_radius;
189 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 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 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 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}