1use super::BlockStateProviderEvaluator;
2use super::noise_cache::NoiseCache;
3use crate::world::LevelReader;
4use smallvec::SmallVec;
5use steel_math::map_clamped;
6use steel_registry::blocks::{
7 BlockRef, block_state_ext::BlockStateExt as _, properties::BlockStateProperties,
8};
9use steel_registry::feature::{
10 BlockHolderSet, BlockStateData, BlockStateProviderKind, DualNoiseProvider,
11 FeatureNoiseParameters, NoiseProvider, NoiseThresholdProvider,
12};
13use steel_registry::{Registry, TaggedRegistryExt as _};
14use steel_utils::random::{Random, RandomSource, legacy_random::LegacyRandom};
15use steel_utils::{BlockPos, BlockStateId, Direction};
16use steel_worldgen::noise::NormalNoise;
17
18pub(crate) trait NoiseScale: Copy {
19 fn scale_coord(self, coord: i32) -> f64;
20}
21
22impl NoiseScale for f32 {
23 fn scale_coord(self, coord: i32) -> f64 {
24 f64::from(coord as f32 * self)
25 }
26}
27
28impl NoiseScale for f64 {
29 fn scale_coord(self, coord: i32) -> f64 {
30 f64::from(coord) * self
31 }
32}
33
34impl BlockStateProviderEvaluator {
35 pub(crate) fn sample_block_state_provider_optional(
36 level: &dyn LevelReader,
37 registry: &Registry,
38 random: &mut impl Random,
39 provider: &BlockStateProviderKind,
40 pos: BlockPos,
41 ) -> Option<BlockStateId> {
42 match provider {
43 BlockStateProviderKind::Reference(provider) => {
44 Self::sample_block_state_provider_optional(
45 level,
46 registry,
47 random,
48 &provider.kind,
49 pos,
50 )
51 }
52 BlockStateProviderKind::RuleBased { fallback, rules } => {
53 for rule in rules {
54 if Self::test_block_predicate(level, registry, &rule.if_true, pos)
55 && let Some(state) = Self::sample_block_state_provider_optional(
56 level, registry, random, &rule.then, pos,
57 )
58 {
59 return Some(state);
60 }
61 }
62
63 fallback.as_ref().and_then(|fallback| {
64 Self::sample_block_state_provider_optional(
65 level, registry, random, fallback, pos,
66 )
67 })
68 }
69 BlockStateProviderKind::RandomBlock { blocks } => {
70 Self::sample_random_block(registry, random, blocks)
71 }
72 _ => Some(Self::sample_block_state_provider(
73 level, registry, random, provider, pos,
74 )),
75 }
76 }
77
78 pub(crate) fn sample_block_state_provider(
79 level: &dyn LevelReader,
80 registry: &Registry,
81 random: &mut impl Random,
82 provider: &BlockStateProviderKind,
83 pos: BlockPos,
84 ) -> BlockStateId {
85 match provider {
86 BlockStateProviderKind::Reference(provider) => {
87 Self::sample_block_state_provider(level, registry, random, &provider.kind, pos)
88 }
89 BlockStateProviderKind::Simple { state } => {
90 Self::block_state_from_data(registry, state)
91 }
92 BlockStateProviderKind::Weighted { entries } => {
93 let total_weight = entries.iter().map(|entry| entry.weight).sum();
94 let mut target = random.next_i32_bounded(total_weight);
95 for entry in entries {
96 if target < entry.weight {
97 return Self::block_state_from_data(registry, &entry.data);
98 }
99 target -= entry.weight;
100 }
101
102 panic!("weighted blockstate provider failed to select an entry");
103 }
104 BlockStateProviderKind::RotatedBlock { state, direction } => {
105 let direction = direction
106 .unwrap_or_else(|| Direction::ALL[random.next_i32_bounded(6) as usize]);
107 let state = Self::sample_block_state_provider(level, registry, random, state, pos);
108 let state = if state.try_get_value(&BlockStateProperties::AXIS).is_some() {
109 state.set_value(&BlockStateProperties::AXIS, direction.axis())
110 } else {
111 state
112 };
113
114 let state = if state.try_get_value(&BlockStateProperties::FACING).is_some() {
115 state.set_value(&BlockStateProperties::FACING, direction)
116 } else {
117 state
118 };
119
120 if direction.is_horizontal()
121 && state
122 .try_get_value(&BlockStateProperties::HORIZONTAL_FACING)
123 .is_some()
124 {
125 state.set_value(&BlockStateProperties::HORIZONTAL_FACING, direction)
126 } else {
127 state
128 }
129 }
130 BlockStateProviderKind::RandomizedInt {
131 property,
132 source,
133 values,
134 } => {
135 let state = Self::sample_block_state_provider(level, registry, random, source, pos);
136 let value = values.sample(random);
137
138 Self::set_int_property_by_name(registry, state, property, value)
139 }
140 BlockStateProviderKind::RuleBased { .. } => {
141 if let Some(state) = Self::sample_block_state_provider_optional(
142 level, registry, random, provider, pos,
143 ) {
144 state
145 } else {
146 level.get_block_state(pos)
147 }
148 }
149 BlockStateProviderKind::Noise(provider) => {
150 Self::sample_noise_provider(registry, provider, pos)
151 }
152 BlockStateProviderKind::NoiseThreshold(provider) => {
153 Self::sample_noise_threshold_provider(registry, random, provider, pos)
154 }
155 BlockStateProviderKind::DualNoise(provider) => {
156 Self::sample_dual_noise_provider(registry, provider, pos)
157 }
158 BlockStateProviderKind::CopyProperties { source } => {
159 let sampled =
160 Self::sample_block_state_provider(level, registry, random, source, pos);
161
162 registry
163 .blocks
164 .with_properties_of(sampled, level.get_block_state(pos))
165 }
166 BlockStateProviderKind::RandomBlock { blocks } => {
167 Self::sample_random_block(registry, random, blocks)
168 .unwrap_or_else(|| level.get_block_state(pos))
169 }
170 }
171 }
172
173 fn sample_random_block(
174 registry: &Registry,
175 random: &mut impl Random,
176 blocks: &BlockHolderSet,
177 ) -> Option<BlockStateId> {
178 let tagged_blocks: SmallVec<[BlockRef; 8]>;
179 let entries = match blocks {
180 BlockHolderSet::Entries(entries) => entries.as_slice(),
181 BlockHolderSet::Tag(tag) => {
182 tagged_blocks = registry.blocks.iter_tag(tag).collect();
183 tagged_blocks.as_slice()
184 }
185 };
186 if entries.is_empty() {
187 return None;
188 }
189 let index = random.next_i32_bounded(entries.len() as i32) as usize;
190 Some(registry.blocks.get_default_state_id(entries[index]))
191 }
192
193 pub(crate) fn set_int_property_by_name(
194 registry: &Registry,
195 state: BlockStateId,
196 property: &str,
197 value: i32,
198 ) -> BlockStateId {
199 let Some(block) = registry.blocks.by_state_id(state) else {
200 panic!("block-state provider received invalid block state id {state:?}");
201 };
202 let value_string = value.to_string();
203 let mut found = false;
204 let properties = registry
205 .blocks
206 .properties(state)
207 .map(|(name, existing)| {
208 if name == property {
209 found = true;
210 (name, value_string.as_str())
211 } else {
212 (name, existing)
213 }
214 })
215 .collect::<Vec<_>>();
216
217 if !found {
218 return state;
219 }
220
221 let Some(new_state) = registry
222 .blocks
223 .state_id_from_block_properties(block, &properties)
224 else {
225 panic!(
226 "randomized int provider produced invalid value {value} for property {property} on {}",
227 block.key
228 );
229 };
230 new_state
231 }
232
233 pub(crate) fn sample_noise_provider(
234 registry: &Registry,
235 provider: &NoiseProvider,
236 pos: BlockPos,
237 ) -> BlockStateId {
238 let noise = NoiseCache::get(&provider.noise, provider.seed);
239 let noise_value = Self::noise_value(&noise, pos, f64::from(provider.scale));
240
241 Self::noise_state_by_value(registry, &provider.states, noise_value)
242 }
243
244 pub(crate) fn sample_noise_threshold_provider(
245 registry: &Registry,
246 random: &mut impl Random,
247 provider: &NoiseThresholdProvider,
248 pos: BlockPos,
249 ) -> BlockStateId {
250 let noise = NoiseCache::get(&provider.noise, provider.seed);
251 let noise_value = Self::noise_value(&noise, pos, f64::from(provider.scale));
252
253 if noise_value < f64::from(provider.threshold) {
254 Self::random_block_state_from_data_list(registry, random, &provider.low_states)
255 } else if random.next_f32() < provider.high_chance {
256 Self::random_block_state_from_data_list(registry, random, &provider.high_states)
257 } else {
258 Self::block_state_from_data(registry, &provider.default_state)
259 }
260 }
261
262 pub(crate) fn sample_dual_noise_provider(
263 registry: &Registry,
264 provider: &DualNoiseProvider,
265 pos: BlockPos,
266 ) -> BlockStateId {
267 let slow_noise = NoiseCache::get(&provider.slow_noise, provider.seed);
268 let variety_noise = Self::noise_value(&slow_noise, pos, provider.slow_scale);
269 let local_variety = map_clamped(
270 variety_noise,
271 -1.0,
272 1.0,
273 f64::from(provider.variety[0]),
274 f64::from(provider.variety[1] + 1),
275 ) as i32;
276 assert!(
277 local_variety > 0,
278 "dual-noise provider local variety must be positive, got {local_variety}"
279 );
280
281 let Ok(capacity) = usize::try_from(local_variety) else {
282 panic!("dual-noise provider local variety {local_variety} exceeds usize range");
283 };
284 let mut possible_states = SmallVec::<[BlockStateId; 8]>::with_capacity(capacity);
285 for i in 0..local_variety {
286 let offset_pos = pos.offset(i * 54_545, 0, i * 34_234);
287 let slow_value = Self::noise_value(&slow_noise, offset_pos, provider.slow_scale);
288 possible_states.push(Self::noise_state_by_value(
289 registry,
290 &provider.states,
291 slow_value,
292 ));
293 }
294
295 let noise = NoiseCache::get(&provider.noise, provider.seed);
296 let noise_value = Self::noise_value(&noise, pos, f64::from(provider.scale));
297
298 Self::noise_state_by_resolved_value(&possible_states, noise_value)
299 }
300
301 pub(crate) fn normal_noise(parameters: &FeatureNoiseParameters, seed: i64) -> NormalNoise {
302 let mut random = RandomSource::Legacy(LegacyRandom::from_seed(seed as u64));
303 NormalNoise::create_from_random_with_params(
304 &mut random,
305 parameters.base_octave,
306 parameters.base_amplitude,
307 parameters.octave_count,
308 parameters.normalize,
309 ¶meters.amplitude_modifiers,
310 )
311 }
312
313 pub(crate) fn noise_value<S: NoiseScale>(noise: &NormalNoise, pos: BlockPos, scale: S) -> f64 {
314 f64::from(noise.get_value(
315 scale.scale_coord(pos.x()),
316 scale.scale_coord(pos.y()),
317 scale.scale_coord(pos.z()),
318 ))
319 }
320
321 pub(crate) fn noise_state_by_value(
322 registry: &Registry,
323 states: &[BlockStateData],
324 noise_value: f64,
325 ) -> BlockStateId {
326 assert!(
327 !states.is_empty(),
328 "noise provider state list must not be empty"
329 );
330 let index = Self::noise_state_index(states.len(), noise_value);
331 Self::block_state_from_data(registry, &states[index])
332 }
333
334 pub(crate) fn noise_state_by_resolved_value(
335 states: &[BlockStateId],
336 noise_value: f64,
337 ) -> BlockStateId {
338 assert!(
339 !states.is_empty(),
340 "noise provider state list must not be empty"
341 );
342 states[Self::noise_state_index(states.len(), noise_value)]
343 }
344
345 pub(crate) fn noise_state_index(state_count: usize, noise_value: f64) -> usize {
346 let placement_value = f32::midpoint(1.0_f32, noise_value as f32).clamp(0.0, 0.9999);
347 (placement_value * state_count as f32) as usize
348 }
349
350 pub(crate) fn random_block_state_from_data_list(
351 registry: &Registry,
352 random: &mut impl Random,
353 states: &[BlockStateData],
354 ) -> BlockStateId {
355 assert!(
356 !states.is_empty(),
357 "random block-state provider state list must not be empty"
358 );
359 let Ok(state_count) = i32::try_from(states.len()) else {
360 panic!(
361 "random block-state provider state count {} exceeds i32 range",
362 states.len()
363 );
364 };
365 let index = random.next_i32_bounded(state_count) as usize;
366 Self::block_state_from_data(registry, &states[index])
367 }
368}