246 lines
8.5 KiB
Rust
246 lines
8.5 KiB
Rust
use core::f32;
|
|
|
|
use pumpkin_data::{BlockDirection, BlockState, BlockStateId};
|
|
use pumpkin_util::{
|
|
math::{lerp, position::BlockPos, vector3::Vector3},
|
|
random::{RandomGenerator, RandomImpl},
|
|
};
|
|
|
|
use crate::generation::proto_chunk::GenerationCache;
|
|
use crate::{generation::rule::RuleTest, world::WorldPortalExt};
|
|
|
|
pub struct OreFeature {
|
|
pub size: i32,
|
|
pub discard_chance_on_air_exposure: f32,
|
|
pub targets: Vec<OreTarget>,
|
|
}
|
|
|
|
pub struct OreTarget {
|
|
pub target: RuleTest,
|
|
pub state: &'static BlockState,
|
|
}
|
|
|
|
impl OreFeature {
|
|
#[expect(clippy::too_many_arguments)]
|
|
pub fn generate<T: GenerationCache>(
|
|
&self,
|
|
chunk: &mut T,
|
|
_block_registry: &dyn WorldPortalExt,
|
|
_min_y: i8,
|
|
_height: u16,
|
|
_feature: pumpkin_data::placed_feature::PlacedFeature, // This placed feature
|
|
random: &mut RandomGenerator,
|
|
pos: BlockPos,
|
|
) -> bool {
|
|
let f = random.next_f32() * f32::consts::PI;
|
|
let g = self.size as f32 / 8.0f32;
|
|
let i = f32::midpoint(self.size as f32 / 16.0f32 * 2.0, 1.0).ceil() as i32;
|
|
|
|
let d = pos.0.x as f64 + f.sin() as f64 * g as f64;
|
|
let e = pos.0.x as f64 - f.sin() as f64 * g as f64;
|
|
let h = pos.0.z as f64 + f.cos() as f64 * g as f64; // Use f.cos() for Math.cos(f)
|
|
let j = pos.0.z as f64 - f.cos() as f64 * g as f64;
|
|
|
|
let l = pos.0.y as f64 + random.next_bounded_i32(3) as f64 - 2.0;
|
|
let m = pos.0.y as f64 + random.next_bounded_i32(3) as f64 - 2.0;
|
|
|
|
let n = pos.0.x - g.ceil() as i32 - i;
|
|
let o = pos.0.y - 2 - i;
|
|
let p = pos.0.z - g.ceil() as i32 - i;
|
|
let q = 2 * (g.ceil() as i32 + i);
|
|
let r = 2 * (2 + i);
|
|
|
|
for _ in n..=(n + q) {
|
|
for _ in p..=(p + q) {
|
|
if o > chunk.ocean_floor_height_exclusive(pos.0.x, pos.0.z) {
|
|
continue;
|
|
}
|
|
return self.generate_vein_part(chunk, random, d, e, h, j, l, m, n, o, p, q, r);
|
|
}
|
|
}
|
|
false
|
|
}
|
|
|
|
#[expect(clippy::too_many_arguments)]
|
|
#[expect(clippy::too_many_lines)]
|
|
fn generate_vein_part<T: GenerationCache>(
|
|
&self,
|
|
chunk: &mut T,
|
|
random: &mut RandomGenerator,
|
|
start_x: f64,
|
|
end_x: f64,
|
|
start_z: f64,
|
|
end_z: f64,
|
|
start_y: f64,
|
|
end_y: f64,
|
|
x_bound: i32,
|
|
y_bound: i32,
|
|
z_bound: i32,
|
|
horizontal_size: i32,
|
|
vertical_size: i32,
|
|
) -> bool {
|
|
let mut placed_blocks_count = 0;
|
|
let bitset_size = (horizontal_size * vertical_size * horizontal_size) as usize;
|
|
let mut bit_set = vec![false; bitset_size];
|
|
let mut mutable_pos = BlockPos::ZERO;
|
|
let j = self.size;
|
|
let mut ds = vec![0.0; (j * 4) as usize];
|
|
for k in 0..j {
|
|
let f = k as f32 / j as f32;
|
|
let d = lerp(f as f64, start_x, end_x);
|
|
let e = lerp(f as f64, start_y, end_y);
|
|
let g = lerp(f as f64, start_z, end_z);
|
|
let h = random.next_f64() * j as f64 / 16.0;
|
|
let l = f32::midpoint(((f32::consts::PI * f).sin() + 1.0) * h as f32, 1.0);
|
|
|
|
ds[k as usize * 4] = d;
|
|
ds[k as usize * 4 + 1] = e;
|
|
ds[k as usize * 4 + 2] = g;
|
|
ds[k as usize * 4 + 3] = l as f64;
|
|
}
|
|
|
|
for k in 0..(j - 1) {
|
|
if ds[k as usize * 4 + 3] <= 0.0 {
|
|
continue;
|
|
}
|
|
for m in (k + 1)..j {
|
|
if ds[m as usize * 4 + 3] <= 0.0 {
|
|
continue;
|
|
}
|
|
let h_val = ds[k as usize * 4 + 3] - ds[m as usize * 4 + 3];
|
|
let d_val = ds[k as usize * 4] - ds[m as usize * 4];
|
|
let e_val = ds[k as usize * 4 + 1] - ds[m as usize * 4 + 1];
|
|
let g_val = ds[k as usize * 4 + 2] - ds[m as usize * 4 + 2];
|
|
|
|
if h_val * h_val > d_val * d_val + e_val * e_val + g_val * g_val {
|
|
if h_val > 0.0 {
|
|
ds[m as usize * 4 + 3] = -1.0;
|
|
continue;
|
|
}
|
|
ds[k as usize * 4 + 3] = -1.0;
|
|
}
|
|
}
|
|
}
|
|
|
|
for m_idx in 0..j {
|
|
let d_val = ds[m_idx as usize * 4 + 3];
|
|
if d_val < 0.0 {
|
|
continue;
|
|
}
|
|
let e_val = ds[m_idx as usize * 4];
|
|
let g_val = ds[m_idx as usize * 4 + 1];
|
|
let h_val = ds[m_idx as usize * 4 + 2];
|
|
|
|
let n_bound = ((e_val - d_val).floor() as i32).max(x_bound);
|
|
let o_bound = ((g_val - d_val).floor() as i32).max(y_bound);
|
|
let p_bound = ((h_val - d_val).floor() as i32).max(z_bound);
|
|
let q_bound = ((e_val + d_val).floor() as i32).max(n_bound);
|
|
let r_bound = ((g_val + d_val).floor() as i32).max(o_bound);
|
|
let s_bound = ((h_val + d_val).floor() as i32).max(p_bound);
|
|
|
|
for t_val in n_bound..=q_bound {
|
|
let u_val = (t_val as f64 + 0.5 - e_val) / d_val;
|
|
if u_val * u_val >= 1.0 {
|
|
continue;
|
|
}
|
|
for v_val in o_bound..=r_bound {
|
|
let w_val = (v_val as f64 + 0.5 - g_val) / d_val;
|
|
if u_val * u_val + w_val * w_val >= 1.0 {
|
|
continue;
|
|
}
|
|
let base_idx = (t_val - x_bound) + (v_val - y_bound) * horizontal_size;
|
|
|
|
for aa_val in p_bound..=s_bound {
|
|
let ab_val = (aa_val as f64 + 0.5 - h_val) / d_val;
|
|
if u_val * u_val + w_val * w_val + ab_val * ab_val >= 1.0 {
|
|
continue;
|
|
}
|
|
if chunk.out_of_height(v_val as i16) {
|
|
continue;
|
|
}
|
|
|
|
let ac = (base_idx + (aa_val - z_bound) * horizontal_size * vertical_size)
|
|
as usize;
|
|
|
|
if bit_set[ac] {
|
|
continue;
|
|
}
|
|
bit_set[ac] = true;
|
|
|
|
mutable_pos.0.x = t_val;
|
|
mutable_pos.0.y = v_val;
|
|
mutable_pos.0.z = aa_val;
|
|
|
|
// if !world.is_valid_for_set_block(&mutable_pos) {
|
|
// continue;
|
|
// }
|
|
|
|
// let ad = t_val;
|
|
// let ae = v_val;
|
|
// let af = aa_val;
|
|
// TODO: using a section would be faster
|
|
|
|
let pos_vec = Vector3::new(t_val, v_val, aa_val);
|
|
let block_state = GenerationCache::get_block_state(chunk, &pos_vec);
|
|
|
|
for target in &self.targets {
|
|
if Self::should_place(
|
|
self.discard_chance_on_air_exposure,
|
|
chunk,
|
|
block_state,
|
|
random,
|
|
target,
|
|
&mutable_pos,
|
|
) {
|
|
chunk.set_block_state(&pos_vec, target.state);
|
|
placed_blocks_count += 1;
|
|
break; // Equivalent to 'continue block11;'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
placed_blocks_count > 0
|
|
}
|
|
|
|
pub fn should_place<T: GenerationCache>(
|
|
discard_chance: f32,
|
|
chunk: &T,
|
|
state: BlockStateId,
|
|
random: &mut RandomGenerator,
|
|
target: &OreTarget,
|
|
pos: &BlockPos,
|
|
) -> bool {
|
|
if !target.target.test(state, random) {
|
|
return false;
|
|
}
|
|
if Self::should_not_discard(random, discard_chance) {
|
|
return true;
|
|
}
|
|
!Self::is_exposed_to_air(chunk, pos)
|
|
}
|
|
|
|
pub fn should_not_discard(random: &mut RandomGenerator, chance: f32) -> bool {
|
|
if chance <= 0.0f32 {
|
|
return true;
|
|
}
|
|
if chance >= 1.0f32 {
|
|
return false;
|
|
}
|
|
random.next_f32() >= chance
|
|
}
|
|
|
|
pub fn is_exposed_to_air<T: GenerationCache>(chunk: &T, pos: &BlockPos) -> bool {
|
|
for dir in BlockDirection::all() {
|
|
if GenerationCache::get_block_state(chunk, &pos.offset(dir.to_offset()).0)
|
|
.to_state()
|
|
.is_air()
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
false
|
|
}
|
|
}
|