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 cold_enough_to_snow: Option<bool>,
45}
46
47impl<'a> SurfaceRuleContext<'a> {
48 #[expect(
50 clippy::too_many_arguments,
51 reason = "surface rule context mirrors vanilla's flat condition input"
52 )]
53 pub fn new(
54 block_x: i32,
55 block_z: i32,
56 surface_depth: i32,
57 surface_secondary: f64,
58 min_surface_level: i32,
59 steep: bool,
60 block_y: i32,
61 stone_depth_above: i32,
62 stone_depth_below: i32,
63 water_height: i32,
64 biome_id: Option<u16>,
65 biome_provider: Option<&'a mut dyn SurfaceBiomeProvider>,
66 system: &'a dyn SurfaceNoiseProvider,
67 condition_noises: &'a SurfaceConditionNoiseCache<'a>,
68 block_states: &'a [BlockStateId],
69 ) -> Self {
70 Self {
71 block_x,
72 block_z,
73 surface_depth,
74 surface_secondary,
75 min_surface_level,
76 steep,
77 block_y,
78 stone_depth_above,
79 stone_depth_below,
80 water_height,
81 biome_id,
82 biome_provider,
83 system,
84 condition_noises,
85 block_states,
86 cold_enough_to_snow: None,
87 }
88 }
89
90 #[must_use]
92 pub fn condition_noise(&self, noise_index: usize) -> f64 {
93 self.condition_noises
94 .get(noise_index, self.system, self.block_x, self.block_z)
95 }
96
97 #[must_use]
99 pub fn condition_noise_3d(&self, noise_index: usize) -> f64 {
100 self.system
101 .condition_noise_3d(noise_index, self.block_x, self.block_y, self.block_z)
102 }
103
104 #[must_use]
106 pub const fn block_state(&self, block_state_index: usize) -> BlockStateId {
107 self.block_states[block_state_index]
108 }
109
110 #[must_use]
112 pub const fn known_biome_id(&self) -> Option<u16> {
113 self.biome_id
114 }
115
116 #[must_use]
118 pub fn biome_id(&mut self) -> Option<u16> {
119 if self.biome_id.is_some() {
120 return self.biome_id;
121 }
122
123 let provider = self.biome_provider.as_mut()?;
124 let biome_id = provider.biome_id(self.block_y);
125 self.biome_id = Some(biome_id);
126 Some(biome_id)
127 }
128
129 #[must_use]
131 pub fn cold_enough_to_snow(&mut self) -> bool {
132 if let Some(value) = self.cold_enough_to_snow {
133 return value;
134 }
135
136 let Some(biome_id) = self.biome_id() else {
137 return false;
138 };
139
140 let value =
141 self.system
142 .cold_enough_to_snow(biome_id, self.block_x, self.block_y, self.block_z);
143 self.cold_enough_to_snow = Some(value);
144 value
145 }
146}
147
148pub trait SurfaceBiomeProvider {
150 fn biome_id(&mut self, block_y: i32) -> u16;
152}
153
154pub struct SurfaceConditionNoiseCache<'a> {
156 values: &'a [Cell<f64>],
157 initialized: &'a [Cell<bool>],
158}
159
160impl<'a> SurfaceConditionNoiseCache<'a> {
161 #[must_use]
163 pub fn new(values: &'a [Cell<f64>], initialized: &'a [Cell<bool>]) -> Self {
164 debug_assert_eq!(values.len(), initialized.len());
165 Self {
166 values,
167 initialized,
168 }
169 }
170
171 pub fn reset(&self) {
173 for initialized in self.initialized {
174 initialized.set(false);
175 }
176 }
177
178 #[must_use]
180 pub fn get(
181 &self,
182 noise_index: usize,
183 system: &dyn SurfaceNoiseProvider,
184 x: i32,
185 z: i32,
186 ) -> f64 {
187 if self.initialized[noise_index].get() {
188 return self.values[noise_index].get();
189 }
190
191 let value = system.condition_noise(noise_index, x, z);
192 self.values[noise_index].set(value);
193 self.initialized[noise_index].set(true);
194 value
195 }
196}
197
198pub trait SurfaceNoiseProvider {
203 fn condition_noise(&self, noise_index: usize, x: i32, z: i32) -> f64;
206
207 fn condition_noise_3d(&self, noise_index: usize, x: i32, y: i32, z: i32) -> f64;
210
211 fn get_band(&self, x: i32, y: i32, z: i32) -> BlockStateId;
213
214 fn cold_enough_to_snow(&self, biome_id: u16, block_x: i32, block_y: i32, block_z: i32) -> bool;
216
217 fn vertical_gradient(
222 &self,
223 gradient_index: usize,
224 block_x: i32,
225 block_y: i32,
226 block_z: i32,
227 true_at_and_below: i32,
228 false_at_and_above: i32,
229 ) -> bool;
230}