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, sprite::SpriteCfg},
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", unsafe(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 let chest = SpriteKind::DungeonChest0;
205 let cfg = SpriteCfg {
206 loot_table: Some("common.loot_tables.spot.jungle_ruin".to_owned()),
207 ..Default::default()
208 };
209 painter.rotated_sprite_with_cfg(chest_pos, chest, 0, cfg);
210 } else {
211 let chest_radius = radius / 2;
212 for n in 1..=(ruins / 4.0) as i32 {
213 let chest_pos = Vec2::new(
214 center.x + (chest_radius as f32 * ((n as f32 * phi).cos())) as i32,
215 center.y + (chest_radius as f32 * ((n as f32 * phi).sin())) as i32,
216 );
217 if RandomField::new(0).get(chest_pos.with_z(plot_base)) % 2 > 0 {
218 for a in 0..8 {
219 painter
220 .aabb(Aabb {
221 min: (chest_pos - 1 - a).with_z(plot_base + 5 - a),
222 max: (chest_pos + 1 + a).with_z(plot_base + 6 - a),
223 })
224 .fill(if a > 1 {
225 stone.clone()
226 } else {
227 weak_stone.clone()
228 });
229 }
230 painter.sprite(chest_pos.with_z(plot_base + 4), SpriteKind::ChestBuried);
231 }
232 }
233 }
234
235 let npc_radius = radius / 4;
237 for n in 1..=(ruins / 4.0) as i32 {
238 let npc_pos = Vec2::new(
239 center.x + (npc_radius as f32 * ((n as f32 * phi).cos())) as i32,
240 center.y + (npc_radius as f32 * ((n as f32 * phi).sin())) as i32,
241 );
242 match RandomField::new(0).get(center.with_z(plot_base)) % 6 {
243 0 => painter.spawn(
245 EntityInfo::at(npc_pos.with_z(plot_base + 5).as_()).with_asset_expect(
246 "common.entity.spot.dwarf_grave_robber",
247 &mut thread_rng,
248 None,
249 ),
250 ),
251 1 => painter.spawn(
253 EntityInfo::at(npc_pos.with_z(plot_base + 5).as_()).with_asset_expect(
254 "common.entity.spot.saurok",
255 &mut thread_rng,
256 None,
257 ),
258 ),
259 2 => painter.spawn(
261 EntityInfo::at(npc_pos.with_z(plot_base + 5).as_()).with_asset_expect(
262 "common.entity.spot.grim_salvager",
263 &mut thread_rng,
264 None,
265 ),
266 ),
267 _ => {},
268 }
269 }
270 }
271}
272
273fn stone_color(block: BlockKind) -> Arc<dyn Fn(Vec3<i32>) -> Option<Block>> {
274 Arc::new(move |pos| {
275 Some(match (RandomField::new(0).get(pos)) % 52 {
276 0..=8 => Block::new(block, Rgb::new(92, 99, 86)),
277 9..=17 => Block::new(block, Rgb::new(83, 89, 78)),
278 18..=26 => Block::new(block, Rgb::new(75, 89, 66)),
279 27..=35 => Block::new(block, Rgb::new(79, 83, 73)),
280 36..=44 => Block::new(block, Rgb::new(66, 80, 59)),
281 _ => Block::new(block, Rgb::new(88, 94, 83)),
282 })
283 })
284}