1use super::prelude::*;
2use super::runner::FeatureDecorationRunner;
3use smallvec::SmallVec;
4use steel_math::map_clamped;
5
6impl FeatureDecorationRunner {
7 pub(super) fn sample_block_state_provider_optional(
8 level: &dyn LevelReader,
9 registry: &Registry,
10 random: &mut WorldgenRandom,
11 provider: &BlockStateProvider,
12 pos: BlockPos,
13 ) -> Option<BlockStateId> {
14 match provider {
15 BlockStateProvider::RuleBased { fallback, rules } => {
16 for rule in rules {
17 if Self::test_block_predicate(level, registry, &rule.if_true, pos) {
18 return Some(Self::sample_block_state_provider(
19 level, registry, random, &rule.then, pos,
20 ));
21 }
22 }
23
24 fallback.as_ref().map(|fallback| {
25 Self::sample_block_state_provider(level, registry, random, fallback, pos)
26 })
27 }
28 _ => Some(Self::sample_block_state_provider(
29 level, registry, random, provider, pos,
30 )),
31 }
32 }
33
34 pub(super) fn sample_block_state_provider(
35 level: &dyn LevelReader,
36 registry: &Registry,
37 random: &mut WorldgenRandom,
38 provider: &BlockStateProvider,
39 pos: BlockPos,
40 ) -> BlockStateId {
41 match provider {
42 BlockStateProvider::Simple { state } => Self::block_state_from_data(registry, state),
43 BlockStateProvider::Weighted { entries } => {
44 assert!(
45 !entries.is_empty(),
46 "weighted block-state provider must not be empty"
47 );
48 let total_weight = entries.iter().fold(0, |total, entry| {
49 assert!(
50 entry.weight > 0,
51 "weighted block-state provider entry weight must be positive, got {}",
52 entry.weight
53 );
54 total + entry.weight
55 });
56 let mut target = random.next_i32_bounded(total_weight);
57 for entry in entries {
58 if target < entry.weight {
59 return Self::block_state_from_data(registry, &entry.data);
60 }
61 target -= entry.weight;
62 }
63 panic!("weighted block-state provider failed to select an entry");
64 }
65 BlockStateProvider::RotatedBlock { state } => {
66 let state = Self::block_state_from_data(registry, state);
67 state.set_value(&BlockStateProperties::AXIS, Self::random_axis(random))
68 }
69 BlockStateProvider::RandomizedInt {
70 property,
71 source,
72 values,
73 } => {
74 let state = Self::sample_block_state_provider(level, registry, random, source, pos);
75 let value = values.sample(random);
76 Self::set_int_property_by_name(registry, state, property, value)
77 }
78 BlockStateProvider::RuleBased { .. } => {
79 if let Some(state) = Self::sample_block_state_provider_optional(
80 level, registry, random, provider, pos,
81 ) {
82 state
83 } else {
84 level.get_block_state(pos)
85 }
86 }
87 BlockStateProvider::Noise(provider) => {
88 Self::sample_noise_provider(registry, provider, pos)
89 }
90 BlockStateProvider::NoiseThreshold(provider) => {
91 Self::sample_noise_threshold_provider(registry, random, provider, pos)
92 }
93 BlockStateProvider::DualNoise(provider) => {
94 Self::sample_dual_noise_provider(registry, provider, pos)
95 }
96 }
97 }
98
99 pub(super) fn random_axis(random: &mut WorldgenRandom) -> Axis {
100 match random.next_i32_bounded(3) {
101 0 => Axis::X,
102 1 => Axis::Y,
103 _ => Axis::Z,
104 }
105 }
106
107 pub(super) fn set_int_property_by_name(
108 registry: &Registry,
109 state: BlockStateId,
110 property: &str,
111 value: i32,
112 ) -> BlockStateId {
113 let Some(block) = registry.blocks.by_state_id(state) else {
114 panic!("block-state provider received invalid block state id {state:?}");
115 };
116 let value_string = value.to_string();
117 let current_properties = registry.blocks.get_properties(state);
118 let mut found = false;
119 let properties = current_properties
120 .iter()
121 .map(|(name, existing)| {
122 if *name == property {
123 found = true;
124 (*name, value_string.as_str())
125 } else {
126 (*name, *existing)
127 }
128 })
129 .collect::<Vec<_>>();
130
131 if !found {
132 return state;
133 }
134
135 let Some(new_state) = registry
136 .blocks
137 .state_id_from_block_properties(block, &properties)
138 else {
139 panic!(
140 "randomized int provider produced invalid value {value} for property {property} on {}",
141 block.key
142 );
143 };
144 new_state
145 }
146
147 pub(super) fn sample_noise_provider(
148 registry: &Registry,
149 provider: &NoiseProvider,
150 pos: BlockPos,
151 ) -> BlockStateId {
152 let noise = Self::normal_noise(&provider.noise, provider.seed);
153 let noise_value = Self::noise_value(&noise, pos, provider.scale);
154 Self::noise_state_by_value(registry, &provider.states, noise_value)
155 }
156
157 pub(super) fn sample_noise_threshold_provider(
158 registry: &Registry,
159 random: &mut WorldgenRandom,
160 provider: &NoiseThresholdProvider,
161 pos: BlockPos,
162 ) -> BlockStateId {
163 let noise = Self::normal_noise(&provider.noise, provider.seed);
164 let noise_value = Self::noise_value(&noise, pos, provider.scale);
165 if noise_value < f64::from(provider.threshold) {
166 Self::random_block_state_from_data_list(registry, random, &provider.low_states)
167 } else if random.next_f32() < provider.high_chance {
168 Self::random_block_state_from_data_list(registry, random, &provider.high_states)
169 } else {
170 Self::block_state_from_data(registry, &provider.default_state)
171 }
172 }
173
174 pub(super) fn sample_dual_noise_provider(
175 registry: &Registry,
176 provider: &DualNoiseProvider,
177 pos: BlockPos,
178 ) -> BlockStateId {
179 let slow_noise = Self::normal_noise(&provider.slow_noise, provider.seed);
180 let variety_noise = Self::noise_value(&slow_noise, pos, provider.slow_scale);
181 let local_variety = map_clamped(
182 variety_noise,
183 -1.0,
184 1.0,
185 f64::from(provider.variety[0]),
186 f64::from(provider.variety[1] + 1),
187 ) as i32;
188 assert!(
189 local_variety > 0,
190 "dual-noise provider local variety must be positive, got {local_variety}"
191 );
192
193 let Ok(capacity) = usize::try_from(local_variety) else {
194 panic!("dual-noise provider local variety {local_variety} exceeds usize range");
195 };
196 let mut possible_states = SmallVec::<[BlockStateId; 8]>::with_capacity(capacity);
197 for i in 0..local_variety {
198 let offset_pos = pos.offset(i * 54_545, 0, i * 34_234);
199 let slow_value = Self::noise_value(&slow_noise, offset_pos, provider.slow_scale);
200 possible_states.push(Self::noise_state_by_value(
201 registry,
202 &provider.states,
203 slow_value,
204 ));
205 }
206
207 let noise = Self::normal_noise(&provider.noise, provider.seed);
208 let noise_value = Self::noise_value(&noise, pos, provider.scale);
209 Self::noise_state_by_resolved_value(&possible_states, noise_value)
210 }
211
212 pub(super) fn normal_noise(parameters: &FeatureNoiseParameters, seed: i64) -> NormalNoise {
213 let mut random = RandomSource::Legacy(LegacyRandom::from_seed(seed as u64));
214 NormalNoise::create_from_random(
215 &mut random,
216 parameters.first_octave,
217 ¶meters.amplitudes,
218 )
219 }
220
221 pub(super) fn noise_value(noise: &NormalNoise, pos: BlockPos, scale: f32) -> f64 {
222 let scale = f64::from(scale);
223 noise.get_value(
224 f64::from(pos.x()) * scale,
225 f64::from(pos.y()) * scale,
226 f64::from(pos.z()) * scale,
227 )
228 }
229
230 pub(super) fn noise_state_by_value(
231 registry: &Registry,
232 states: &[BlockStateData],
233 noise_value: f64,
234 ) -> BlockStateId {
235 assert!(
236 !states.is_empty(),
237 "noise provider state list must not be empty"
238 );
239 let index = Self::noise_state_index(states.len(), noise_value);
240 Self::block_state_from_data(registry, &states[index])
241 }
242
243 pub(super) fn noise_state_by_resolved_value(
244 states: &[BlockStateId],
245 noise_value: f64,
246 ) -> BlockStateId {
247 assert!(
248 !states.is_empty(),
249 "noise provider state list must not be empty"
250 );
251 states[Self::noise_state_index(states.len(), noise_value)]
252 }
253
254 pub(super) fn noise_state_index(state_count: usize, noise_value: f64) -> usize {
255 let placement_value = f64::midpoint(1.0, noise_value).clamp(0.0, 0.9999);
256 (placement_value * state_count as f64) as usize
257 }
258
259 pub(super) fn random_block_state_from_data_list(
260 registry: &Registry,
261 random: &mut WorldgenRandom,
262 states: &[BlockStateData],
263 ) -> BlockStateId {
264 assert!(
265 !states.is_empty(),
266 "random block-state provider state list must not be empty"
267 );
268 let Ok(state_count) = i32::try_from(states.len()) else {
269 panic!(
270 "random block-state provider state count {} exceeds i32 range",
271 states.len()
272 );
273 };
274 let index = random.next_i32_bounded(state_count) as usize;
275 Self::block_state_from_data(registry, &states[index])
276 }
277}
278
279#[cfg(test)]
280mod tests {
281 use super::FeatureDecorationRunner;
282
283 #[test]
284 fn noise_state_index_uses_vanilla_placement_value_formula() {
285 for (state_count, noise_value) in
286 [(2, -1.5), (4, -0.5), (8, 0.0), (16, 0.75), (32, 1.5)] as [(usize, f64); 5]
287 {
288 let placement_value = f64::midpoint(1.0, noise_value).clamp(0.0, 0.9999);
289 let expected = (placement_value * state_count as f64) as usize;
290
291 assert_eq!(
292 FeatureDecorationRunner::noise_state_index(state_count, noise_value),
293 expected
294 );
295 }
296 }
297}