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, } pub struct OreTarget { pub target: RuleTest, pub state: &'static BlockState, } impl OreFeature { #[expect(clippy::too_many_arguments)] pub fn generate( &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( &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( 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(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 } }