1use std::sync::OnceLock;
9
10use rustc_hash::FxHashMap;
11use steel_utils::Identifier;
12use steel_utils::random::{Random, legacy_random::LegacyRandom};
13use steel_utils::value_providers::{FloatProvider, HeightProvider, IntProvider};
14
15#[derive(Debug, Clone)]
22pub struct CarverConfiguration {
23 pub probability: f32,
25 pub y: HeightProvider,
27}
28
29#[derive(Debug, Clone)]
31pub struct CaveCarverConfiguration {
32 pub base: CarverConfiguration,
34 pub count: IntProvider,
36 pub thickness: FloatProvider,
38 pub weird_thickness_bias: bool,
40 pub room_vertical_radius_multiplier: FloatProvider,
42 pub horizontal_radius_multiplier: FloatProvider,
44 pub vertical_radius_multiplier: FloatProvider,
46 pub start_vertical_radius_multiplier: FloatProvider,
48 pub floor_level: FloatProvider,
50}
51
52#[derive(Debug, Clone)]
55pub struct CanyonShapeConfiguration {
56 pub distance_factor: FloatProvider,
58 pub thickness: FloatProvider,
60 pub width_smoothness: i32,
62 pub horizontal_radius_factor: FloatProvider,
64 pub vertical_radius_default_factor: f32,
66 pub vertical_radius_center_factor: f32,
68 pub y_scale: FloatProvider,
70}
71
72impl CanyonShapeConfiguration {
73 #[must_use]
77 pub fn init_width_factors(&self, gen_depth: i32, random: &mut LegacyRandom) -> Vec<f32> {
78 let depth = gen_depth as usize;
79 let mut factors = vec![0.0_f32; depth];
80 let mut current = 1.0_f32;
81 for (y_index, slot) in factors.iter_mut().enumerate() {
82 if y_index == 0 || random.next_i32_bounded(self.width_smoothness) == 0 {
83 current = 1.0 + random.next_f32() * random.next_f32();
84 }
85 *slot = current * current;
86 }
87 factors
88 }
89
90 #[must_use]
94 pub fn update_vertical_radius(
95 &self,
96 random: &mut LegacyRandom,
97 vertical_radius: f64,
98 distance: f32,
99 current_step: f32,
100 ) -> f64 {
101 let vertical_multiplier = 1.0_f32 - (0.5 - current_step / distance).abs() * 2.0;
103 let factor = self.vertical_radius_default_factor
104 + self.vertical_radius_center_factor * vertical_multiplier;
105 let jitter = 0.75 + random.next_f32() * 0.25;
107 f64::from(factor) * vertical_radius * f64::from(jitter)
108 }
109}
110
111#[derive(Debug, Clone)]
113pub struct CanyonCarverConfiguration {
114 pub base: CarverConfiguration,
116 pub vertical_rotation: FloatProvider,
118 pub shape: CanyonShapeConfiguration,
120}
121
122#[derive(Debug, Clone)]
125pub enum ConfiguredCarverKind {
126 Cave(CaveCarverConfiguration),
128 Canyon(CanyonCarverConfiguration),
130}
131
132#[derive(Debug)]
137pub struct ConfiguredCarver {
138 pub key: Identifier,
140 pub kind: ConfiguredCarverKind,
142 pub id: OnceLock<usize>,
145}
146
147impl ConfiguredCarver {
148 #[must_use]
150 pub const fn base(&self) -> &CarverConfiguration {
151 match &self.kind {
152 ConfiguredCarverKind::Cave(c) => &c.base,
153 ConfiguredCarverKind::Canyon(c) => &c.base,
154 }
155 }
156}
157
158pub type ConfiguredCarverRef = &'static ConfiguredCarver;
160
161pub struct ConfiguredCarverRegistry {
163 carvers_by_id: Vec<ConfiguredCarverRef>,
164 carvers_by_key: FxHashMap<Identifier, usize>,
165 allows_registering: bool,
166}
167
168impl ConfiguredCarverRegistry {
169 #[must_use]
171 pub fn new() -> Self {
172 Self {
173 carvers_by_id: Vec::new(),
174 carvers_by_key: FxHashMap::default(),
175 allows_registering: true,
176 }
177 }
178
179 pub fn register(&mut self, entry: ConfiguredCarverRef) -> usize {
181 assert!(
182 self.allows_registering,
183 "Cannot register ConfiguredCarver after registry has been frozen"
184 );
185 let id = self.carvers_by_id.len();
186 let cached = entry.id.get_or_init(|| id);
187 assert_eq!(*cached, id, "carver registered with conflicting id");
188 self.carvers_by_id.push(entry);
189 self.carvers_by_key.insert(entry.key.clone(), id);
190 id
191 }
192
193 pub fn iter(&self) -> impl Iterator<Item = (usize, ConfiguredCarverRef)> + '_ {
195 self.carvers_by_id
196 .iter()
197 .enumerate()
198 .map(|(id, &entry)| (id, entry))
199 }
200}
201
202impl Default for ConfiguredCarverRegistry {
203 fn default() -> Self {
204 Self::new()
205 }
206}
207
208crate::impl_registry_ext!(
209 ConfiguredCarverRegistry,
210 ConfiguredCarver,
211 carvers_by_id,
212 carvers_by_key
213);
214
215crate::impl_registry_entry_eq!(ConfiguredCarver);
216
217impl crate::RegistryEntry for ConfiguredCarver {
218 fn key(&self) -> &Identifier {
219 &self.key
220 }
221
222 fn try_id(&self) -> Option<usize> {
223 self.id.get().copied()
224 }
225}