veloren_world/site2/plot/
jungle_ruin.rs1use super::*;
2use crate::{
3 Land,
4 assets::AssetHandle,
5 site2::gen::PrimitiveTransform,
6 util::{RandomField, sampler::Sampler},
7};
8use common::{
9 generation::EntityInfo,
10 terrain::{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 JungleRuin {
18 bounds: Aabr<i32>,
19 pub(crate) alt: i32,
20}
21impl JungleRuin {
22 pub fn generate(land: &Land, _rng: &mut impl Rng, site: &Site, tile_aabr: Aabr<i32>) -> Self {
23 let bounds = Aabr {
24 min: site.tile_wpos(tile_aabr.min),
25 max: site.tile_wpos(tile_aabr.max),
26 };
27 Self {
28 bounds,
29 alt: land.get_alt_approx(site.tile_center_wpos((tile_aabr.max - tile_aabr.min) / 2))
30 as i32
31 + 2,
32 }
33 }
34}
35
36impl Structure for JungleRuin {
37 #[cfg(feature = "use-dyn-lib")]
38 const UPDATE_FN: &'static [u8] = b"render_jungleruin\0";
39
40 #[cfg_attr(feature = "be-dyn-lib", export_name = "render_jungleruin")]
41 fn render_inner(&self, _site: &Site, land: &Land, painter: &Painter) {
42 let center = self.bounds.center();
43 let plot_base = land.get_alt_approx(center) as i32;
44 let mut thread_rng = thread_rng();
45 let stone = Fill::Sampling(stone_color(BlockKind::Rock));
46 let weak_stone = Fill::Sampling(stone_color(BlockKind::WeakRock));
47 let stone_broken = Fill::Sampling(Arc::new(|center| {
48 Some(match (RandomField::new(0).get(center)) % 56 {
49 0..=8 => Block::new(BlockKind::Rock, Rgb::new(92, 99, 86)),
50 9..=17 => Block::new(BlockKind::Rock, Rgb::new(83, 89, 78)),
51 18..=26 => Block::new(BlockKind::Rock, Rgb::new(75, 89, 66)),
52 27..=35 => Block::new(BlockKind::Rock, Rgb::new(79, 83, 73)),
53 36..=44 => Block::new(BlockKind::Rock, Rgb::new(66, 80, 59)),
54 45..=49 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
55 _ => Block::new(BlockKind::Rock, Rgb::new(88, 94, 83)),
56 })
57 }));
58 let grass_fill = Fill::Sampling(Arc::new(|wpos| {
59 Some(match (RandomField::new(0).get(wpos)) % 30 {
60 1..=2 => Block::air(SpriteKind::ShortGrass),
61 3..=7 => Block::air(SpriteKind::LongGrass),
62 8 => Block::air(SpriteKind::JungleFern),
63 _ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
64 })
65 }));
66 let mut ruin_positions = vec![];
67 let pos_var = RandomField::new(0).get(center.with_z(plot_base)) % 10;
68 let radius = 25 + pos_var;
69 let ruins = 12.0 + pos_var as f32;
70 let phi = TAU / ruins;
71 for n in 1..=ruins as i32 {
72 let pos = Vec2::new(
73 center.x + (radius as f32 * ((n as f32 * phi).cos())) as i32,
74 center.y + (radius as f32 * ((n as f32 * phi).sin())) as i32,
75 );
76 let base = land.get_alt_approx(pos) as i32;
77 let ground_sink = RandomField::new(0).get(pos.with_z(base)) as i32 % 4;
78 let ruin_pos = pos.with_z(base - 8 - ground_sink);
79 ruin_positions.push(ruin_pos);
80 }
81 let underground_chamber = pos_var < 7;
82 let room_size = 10;
84 let height_handle = 10;
85 if underground_chamber {
86 painter
88 .aabb(Aabb {
89 min: (center - room_size - 1).with_z(plot_base - height_handle - room_size - 1),
90 max: (center + room_size + 1).with_z(plot_base - height_handle + room_size + 1),
91 })
92 .fill(stone.clone());
93 painter
94 .aabb(Aabb {
95 min: (center - room_size).with_z(plot_base - height_handle - room_size),
96 max: (center + room_size).with_z(plot_base - height_handle + room_size + 2),
97 })
98 .fill(stone_broken.clone());
99 painter
101 .aabb(Aabb {
102 min: (center - room_size + 1).with_z(plot_base - height_handle + room_size + 1),
103 max: (center + room_size - 1).with_z(plot_base - height_handle + room_size + 2),
104 })
105 .clear();
106 let center_ruin_pos = center.with_z(plot_base - 1);
107 ruin_positions.push(center_ruin_pos);
108
109 for d in 0..=5 {
111 painter
112 .line(
113 Vec2::new(center.x, center.y - room_size + 1)
114 .with_z(plot_base - height_handle - (room_size / 3) + 3),
115 Vec2::new(center.x, center.y + room_size - 1)
116 .with_z(plot_base - height_handle - (room_size / 3) + 3),
117 (room_size - (2 * d)) as f32,
118 )
119 .fill(stone_broken.clone());
120 painter
121 .line(
122 Vec2::new(center.x, center.y - room_size + 1)
123 .with_z(plot_base - height_handle - (room_size / 3) + 3),
124 Vec2::new(center.x, center.y + room_size - 1)
125 .with_z(plot_base - height_handle - (room_size / 3) + 3),
126 (room_size - 1 - (2 * d)) as f32,
127 )
128 .clear();
129 painter
130 .line(
131 Vec2::new(center.x - room_size + 1, center.y)
132 .with_z(plot_base - height_handle - (room_size / 3) + 3),
133 Vec2::new(center.x + room_size - 1, center.y)
134 .with_z(plot_base - height_handle - (room_size / 3) + 3),
135 (room_size - (2 * d)) as f32,
136 )
137 .fill(stone_broken.clone());
138 painter
139 .line(
140 Vec2::new(center.x - room_size + 1, center.y)
141 .with_z(plot_base - height_handle - (room_size / 3) + 3),
142 Vec2::new(center.x + room_size - 1, center.y)
143 .with_z(plot_base - height_handle - (room_size / 3) + 3),
144 (room_size - 1 - (2 * d)) as f32,
145 )
146 .clear();
147 }
148 painter
150 .aabb(Aabb {
151 min: (center - room_size).with_z(plot_base - height_handle - room_size),
152 max: (center + room_size).with_z(plot_base - height_handle + room_size - 1),
153 })
154 .clear();
155 painter
156 .aabb(Aabb {
157 min: (center - room_size).with_z(plot_base - height_handle - room_size),
158 max: (center + room_size).with_z(plot_base - height_handle - room_size + 1),
159 })
160 .fill(stone.clone());
161 painter
162 .aabb(Aabb {
163 min: (center - room_size).with_z(plot_base - height_handle - room_size + 1),
164 max: (center + room_size).with_z(plot_base - height_handle - room_size + 2),
165 })
166 .fill(grass_fill);
167 }
168 for ruin_pos in ruin_positions {
169 lazy_static! {
171 pub static ref RUIN: AssetHandle<StructuresGroup> =
172 PrefabStructure::load_group("site_structures.jungle_ruin.jungle_ruin");
173 }
174 let rng = RandomField::new(0).get(ruin_pos) % 62;
175 let ruin = RUIN.read();
176 let ruin = ruin[rng as usize % ruin.len()].clone();
177 painter
178 .prim(Primitive::Prefab(Box::new(ruin.clone())))
179 .translate(ruin_pos)
180 .fill(Fill::Prefab(Box::new(ruin), ruin_pos, rng));
181 }
182 if underground_chamber {
183 painter
185 .aabb(Aabb {
186 min: Vec2::new(center.x - 9, center.y - 3)
187 .with_z(plot_base - height_handle - room_size + 1),
188 max: Vec2::new(center.x - 3, center.y + 3).with_z(plot_base + 30),
189 })
190 .clear();
191 painter
193 .ramp(
194 Aabb {
195 min: Vec2::new(center.x - room_size, center.y - 3)
196 .with_z(plot_base - height_handle - room_size + 1),
197 max: Vec2::new(center.x, center.y + 3).with_z(plot_base),
198 },
199 Dir::NegX,
200 )
201 .fill(stone_broken);
202 let chest_pos = Vec2::new(center.x + room_size - 2, center.y - 3)
203 .with_z(plot_base - height_handle - room_size + 1);
204 painter.sprite(chest_pos, SpriteKind::DungeonChest0);
205 } else {
206 let chest_radius = radius / 2;
207 for n in 1..=(ruins / 4.0) as i32 {
208 let chest_pos = Vec2::new(
209 center.x + (chest_radius as f32 * ((n as f32 * phi).cos())) as i32,
210 center.y + (chest_radius as f32 * ((n as f32 * phi).sin())) as i32,
211 );
212 if RandomField::new(0).get(chest_pos.with_z(plot_base)) % 2 > 0 {
213 for a in 0..8 {
214 painter
215 .aabb(Aabb {
216 min: (chest_pos - 1 - a).with_z(plot_base + 5 - a),
217 max: (chest_pos + 1 + a).with_z(plot_base + 6 - a),
218 })
219 .fill(if a > 1 {
220 stone.clone()
221 } else {
222 weak_stone.clone()
223 });
224 }
225 painter.sprite(chest_pos.with_z(plot_base + 4), SpriteKind::ChestBuried);
226 }
227 }
228 }
229
230 let npc_radius = radius / 4;
232 for n in 1..=(ruins / 4.0) as i32 {
233 let npc_pos = Vec2::new(
234 center.x + (npc_radius as f32 * ((n as f32 * phi).cos())) as i32,
235 center.y + (npc_radius as f32 * ((n as f32 * phi).sin())) as i32,
236 );
237 match RandomField::new(0).get(center.with_z(plot_base)) % 6 {
238 0 => painter.spawn(
240 EntityInfo::at(npc_pos.with_z(plot_base + 5).as_()).with_asset_expect(
241 "common.entity.spot.dwarf_grave_robber",
242 &mut thread_rng,
243 None,
244 ),
245 ),
246 1 => painter.spawn(
248 EntityInfo::at(npc_pos.with_z(plot_base + 5).as_()).with_asset_expect(
249 "common.entity.spot.saurok",
250 &mut thread_rng,
251 None,
252 ),
253 ),
254 2 => painter.spawn(
256 EntityInfo::at(npc_pos.with_z(plot_base + 5).as_()).with_asset_expect(
257 "common.entity.spot.grim_salvager",
258 &mut thread_rng,
259 None,
260 ),
261 ),
262 _ => {},
263 }
264 }
265 }
266}
267
268fn stone_color(block: BlockKind) -> Arc<dyn Fn(Vec3<i32>) -> Option<Block>> {
269 Arc::new(move |pos| {
270 Some(match (RandomField::new(0).get(pos)) % 52 {
271 0..=8 => Block::new(block, Rgb::new(92, 99, 86)),
272 9..=17 => Block::new(block, Rgb::new(83, 89, 78)),
273 18..=26 => Block::new(block, Rgb::new(75, 89, 66)),
274 27..=35 => Block::new(block, Rgb::new(79, 83, 73)),
275 36..=44 => Block::new(block, Rgb::new(66, 80, 59)),
276 _ => Block::new(block, Rgb::new(88, 94, 83)),
277 })
278 })
279}