use crate::util::rng::RandomSource;
use crate::world::worldgen::block_predicate::BlockPredicate;
use crate::world::worldgen::feature::features::mark_above_for_post_processing;
use crate::world::worldgen::feature::state_provider::ProviderHolder;
use crate::world::worldgen::region::WorldGenRegion;
use serde::Deserialize;
use shulkr::util::BlockPosition;
use shulkr::world::block::Block;
#[derive(Debug, Deserialize)]
pub struct LakeConfig {
pub fluid: ProviderHolder,
pub barrier: ProviderHolder,
pub can_place_feature: BlockPredicate,
pub can_replace_with_air_or_fluid: BlockPredicate,
pub can_replace_with_barrier: BlockPredicate,
}
const GRID_LEN: usize = 2048;
const fn index(xx: usize, zz: usize, yy: usize) -> usize {
(xx * 16 + zz) * 8 + yy
}
const fn borders_grid(grid: &[bool; GRID_LEN], xx: usize, zz: usize, yy: usize) -> bool {
!grid[index(xx, zz, yy)]
&& (xx < 15 && grid[index(xx + 1, zz, yy)]
|| xx > 0 && grid[index(xx - 1, zz, yy)]
|| zz < 15 && grid[index(xx, zz + 1, yy)]
|| zz > 0 && grid[index(xx, zz - 1, yy)]
|| yy < 7 && grid[index(xx, zz, yy + 1)]
|| yy > 0 && grid[index(xx, zz, yy - 1)])
}
pub fn place(
region: &WorldGenRegion<'_>,
random: &mut RandomSource,
origin: BlockPosition,
config: &LakeConfig,
) -> bool {
if origin.y() <= region.min_y() + 4 {
return false;
}
let origin = origin.offset(-8, -4, -8);
let mut grid = [false; GRID_LEN];
let spots = random.next_i32_bounded(4) + 4;
for _ in 0..spots {
let xr = random.next_f64() * 6.0 + 3.0;
let yr = random.next_f64() * 4.0 + 2.0;
let zr = random.next_f64() * 6.0 + 3.0;
let xp = random.next_f64() * (16.0 - xr - 2.0) + 1.0 + xr / 2.0;
let yp = random.next_f64() * (8.0 - yr - 4.0) + 2.0 + yr / 2.0;
let zp = random.next_f64() * (16.0 - zr - 2.0) + 1.0 + zr / 2.0;
for xx in 1..15usize {
for zz in 1..15usize {
for yy in 1..7usize {
let xd = (xx as f64 - xp) / (xr / 2.0);
let yd = (yy as f64 - yp) / (yr / 2.0);
let zd = (zz as f64 - zp) / (zr / 2.0);
if xd * xd + yd * yd + zd * zd < 1.0 {
grid[index(xx, zz, yy)] = true;
}
}
}
}
}
let fluid = config.fluid.get_state(region, random, origin);
for xx in 0..16usize {
for zz in 0..16usize {
for yy in 0..8usize {
if !borders_grid(&grid, xx, zz, yy) {
continue;
}
let pos = origin.offset(xx as i32, yy as i32, zz as i32);
let state = region.get_block(pos);
if yy >= 4 && state.liquid() {
return false;
}
if yy < 4 && !state.is_solid() && state != fluid {
return false;
}
if !config.can_place_feature.test(region, pos) {
return false;
}
}
}
}
let cave_air = Block::CAVE_AIR.default_state();
for xx in 0..16usize {
for zz in 0..16usize {
for yy in 0..8usize {
if !grid[index(xx, zz, yy)] {
continue;
}
let pos = origin.offset(xx as i32, yy as i32, zz as i32);
if config.can_replace_with_air_or_fluid.test(region, pos) {
let place_air = yy >= 4;
region.set_block(pos, if place_air { cave_air } else { fluid });
if place_air {
mark_above_for_post_processing(region, pos);
}
}
}
}
}
let barrier = config.barrier.get_state(region, random, origin);
if !barrier.is_air() {
for xx in 0..16usize {
for zz in 0..16usize {
for yy in 0..8usize {
if !borders_grid(&grid, xx, zz, yy) {
continue;
}
if yy >= 4 && random.next_i32_bounded(2) == 0 {
continue;
}
let pos = origin.offset(xx as i32, yy as i32, zz as i32);
let state = region.get_block(pos);
if state.is_solid() && config.can_replace_with_barrier.test(region, pos) {
region.set_block(pos, barrier);
mark_above_for_post_processing(region, pos);
}
}
}
}
}
true
}