1use std::cell::Cell;
4
5use crate::BlockStateId;
6
7pub struct SurfaceRuleContext<'a> {
13 pub block_x: i32,
15 pub block_z: i32,
17 pub surface_depth: i32,
19 pub surface_secondary: f64,
21 pub min_surface_level: i32,
23 pub steep: bool,
25 pub block_y: i32,
27 pub stone_depth_above: i32,
29 pub stone_depth_below: i32,
31 pub water_height: i32,
33 biome_id: Option<u16>,
35 biome_provider: Option<&'a mut dyn SurfaceBiomeProvider>,
37 pub system: &'a dyn SurfaceNoiseProvider,
39 condition_noises: &'a SurfaceConditionNoiseCache<'a>,
41 block_states: &'a [BlockStateId],
43 ore_vein_results: &'a [Option<BlockStateId>],
45 cold_enough_to_snow: Option<bool>,
47}
48
49impl<'a> SurfaceRuleContext<'a> {
50 #[expect(
52 clippy::too_many_arguments,
53 reason = "surface rule context mirrors vanilla's flat condition input"
54 )]
55 pub fn new(
56 block_x: i32,
57 block_z: i32,
58 surface_depth: i32,
59 surface_secondary: f64,
60 min_surface_level: i32,
61 steep: bool,
62 block_y: i32,
63 stone_depth_above: i32,
64 stone_depth_below: i32,
65 water_height: i32,
66 biome_id: Option<u16>,
67 biome_provider: Option<&'a mut dyn SurfaceBiomeProvider>,
68 system: &'a dyn SurfaceNoiseProvider,
69 condition_noises: &'a SurfaceConditionNoiseCache<'a>,
70 block_states: &'a [BlockStateId],
71 ) -> Self {
72 Self {
73 block_x,
74 block_z,
75 surface_depth,
76 surface_secondary,
77 min_surface_level,
78 steep,
79 block_y,
80 stone_depth_above,
81 stone_depth_below,
82 water_height,
83 biome_id,
84 biome_provider,
85 system,
86 condition_noises,
87 block_states,
88 ore_vein_results: &[],
89 cold_enough_to_snow: None,
90 }
91 }
92
93 #[must_use]
95 pub const fn with_ore_vein_results(mut self, results: &'a [Option<BlockStateId>]) -> Self {
96 self.ore_vein_results = results;
97 self
98 }
99
100 #[must_use]
102 pub fn condition_noise(&self, noise_index: usize) -> f64 {
103 self.condition_noises
104 .get(noise_index, self.system, self.block_x, self.block_z)
105 }
106
107 #[must_use]
109 pub fn condition_noise_3d(&self, noise_index: usize) -> f64 {
110 self.system
111 .condition_noise_3d(noise_index, self.block_x, self.block_y, self.block_z)
112 }
113
114 #[must_use]
116 pub const fn block_state(&self, block_state_index: usize) -> BlockStateId {
117 self.block_states[block_state_index]
118 }
119
120 #[must_use]
122 pub fn ore_vein_result(&self, ore_vein_index: usize) -> Option<BlockStateId> {
123 match self.ore_vein_results.get(ore_vein_index) {
124 Some(result) => *result,
125 None => None,
126 }
127 }
128
129 #[must_use]
131 pub const fn known_biome_id(&self) -> Option<u16> {
132 self.biome_id
133 }
134
135 #[must_use]
137 pub fn biome_id(&mut self) -> Option<u16> {
138 if self.biome_id.is_some() {
139 return self.biome_id;
140 }
141
142 let provider = self.biome_provider.as_mut()?;
143 let biome_id = provider.biome_id(self.block_y);
144 self.biome_id = Some(biome_id);
145 Some(biome_id)
146 }
147
148 #[must_use]
150 pub fn cold_enough_to_snow(&mut self) -> bool {
151 if let Some(value) = self.cold_enough_to_snow {
152 return value;
153 }
154
155 let Some(biome_id) = self.biome_id() else {
156 return false;
157 };
158
159 let value =
160 self.system
161 .cold_enough_to_snow(biome_id, self.block_x, self.block_y, self.block_z);
162 self.cold_enough_to_snow = Some(value);
163 value
164 }
165}
166
167pub trait SurfaceBiomeProvider {
169 fn biome_id(&mut self, block_y: i32) -> u16;
171}
172
173pub struct SurfaceConditionNoiseCache<'a> {
175 values: &'a [Cell<f64>],
176 initialized: &'a [Cell<bool>],
177}
178
179impl<'a> SurfaceConditionNoiseCache<'a> {
180 #[must_use]
182 pub fn new(values: &'a [Cell<f64>], initialized: &'a [Cell<bool>]) -> Self {
183 debug_assert_eq!(values.len(), initialized.len());
184 Self {
185 values,
186 initialized,
187 }
188 }
189
190 pub fn reset(&self) {
192 for initialized in self.initialized {
193 initialized.set(false);
194 }
195 }
196
197 #[must_use]
199 pub fn get(
200 &self,
201 noise_index: usize,
202 system: &(impl SurfaceNoiseProvider + ?Sized),
203 x: i32,
204 z: i32,
205 ) -> f64 {
206 if self.initialized[noise_index].get() {
207 return self.values[noise_index].get();
208 }
209
210 let value = system.condition_noise(noise_index, x, z);
211 self.values[noise_index].set(value);
212 self.initialized[noise_index].set(true);
213 value
214 }
215}
216
217pub trait SurfaceNoiseProvider {
222 fn condition_noise(&self, noise_index: usize, x: i32, z: i32) -> f64;
225
226 fn condition_noise_3d(&self, noise_index: usize, x: i32, y: i32, z: i32) -> f64;
229
230 fn get_band(&self, x: i32, y: i32, z: i32) -> BlockStateId;
232
233 fn cold_enough_to_snow(&self, biome_id: u16, block_x: i32, block_y: i32, block_z: i32) -> bool;
235
236 fn vertical_gradient(
241 &self,
242 gradient_index: usize,
243 block_x: i32,
244 block_y: i32,
245 block_z: i32,
246 true_at_and_below: i32,
247 false_at_and_above: i32,
248 ) -> bool;
249}