veloren_world/site2/plot/
jungle_ruin.rs

1use 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        // center ruin and underground chest chamber
83        let room_size = 10;
84        let height_handle = 10;
85        if underground_chamber {
86            // room
87            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            // platform
100            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            // room decor
110            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            // clear room
149            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            // ruin models
170            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            // entry
184            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            // stairs
192            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        // npcs
231        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                // grave robbers
239                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                // sauroks
247                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                // grim salvager
255                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}