Pumpkin/crates/pumpkin-world/src/generation/feature/features/ore.rs
2026-09-02 17:36:22 +02:00

245 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 xprobe in n..=(n + q) {
for zprobe in p..=(p + q) {
if o <= chunk.ocean_floor_height_exclusive(xprobe, zprobe) {
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
}
}