1use rustc_hash::FxHashMap;
2use simdnbt::ToNbtTag;
3use simdnbt::owned::NbtTag;
4use steel_utils::Identifier;
5
6use crate::sound_event::SoundEventRef;
7
8#[derive(Debug)]
9pub struct BedRule {
10 pub can_set_spawn: &'static str,
11 pub can_sleep: &'static str,
12 pub explodes: bool,
13 pub error_message_key: Option<&'static str>,
14}
15
16#[derive(Debug)]
17pub struct MoodSound {
18 pub sound: SoundEventRef,
19 pub tick_delay: i32,
20 pub block_search_extent: i32,
21 pub offset: f64,
22}
23
24#[derive(Debug)]
25pub struct MusicEntry {
26 pub sound: SoundEventRef,
27 pub min_delay: i32,
28 pub max_delay: i32,
29 pub replace_current_music: bool,
30}
31
32#[derive(Debug)]
33pub struct BackgroundMusic {
34 pub default: MusicEntry,
35 pub creative: Option<MusicEntry>,
36}
37
38#[derive(Debug)]
40pub struct DimensionType {
41 pub key: Identifier,
42 pub fixed_time: Option<i64>,
43 pub has_skylight: bool,
44 pub has_ceiling: bool,
45 pub coordinate_scale: f64,
46 pub min_y: i32,
47 pub height: i32,
48 pub logical_height: i32,
49 pub infiniburn: &'static str,
50 pub ambient_light: f32,
51 pub default_clock: Option<&'static str>,
52 pub timelines: Option<&'static str>,
53 pub has_ender_dragon_fight: bool,
54 pub monster_spawn_light_level: MonsterSpawnLightLevel,
55 pub monster_spawn_block_light_limit: i32,
56
57 pub skybox: Option<&'static str>,
59 pub cardinal_light: Option<&'static str>,
60
61 pub sky_color: Option<&'static str>,
63 pub fog_color: Option<&'static str>,
64 pub cloud_color: Option<&'static str>,
65 pub cloud_height: Option<f32>,
66 pub ambient_light_color: Option<&'static str>,
67 pub sky_light_color: Option<&'static str>,
68 pub sky_light_factor: Option<f32>,
69 pub fog_start_distance: Option<f32>,
70 pub fog_end_distance: Option<f32>,
71 pub default_dripstone_particle: Option<&'static str>,
72
73 pub respawn_anchor_works: bool,
75 pub can_start_raid: bool,
76 pub fast_lava: bool,
77 pub piglins_zombify: bool,
78 pub sky_light_level: Option<f32>,
79 pub snow_golem_melts: bool,
80 pub water_evaporates: bool,
81 pub nether_portal_spawns_piglin: bool,
82 pub bed_rule: BedRule,
83
84 pub mood_sound: Option<MoodSound>,
86 pub background_music: Option<BackgroundMusic>,
87}
88
89#[derive(Debug)]
91pub enum MonsterSpawnLightLevel {
92 Simple(i32),
93 Complex {
94 distribution_type: &'static str,
95 min_inclusive: i32,
96 max_inclusive: i32,
97 },
98}
99
100impl ToNbtTag for &DimensionType {
101 fn to_nbt_tag(self) -> NbtTag {
102 use simdnbt::owned::{NbtCompound, NbtTag};
103 let mut compound = NbtCompound::new();
104
105 if let Some(fixed_time) = self.fixed_time {
107 compound.insert("fixed_time", fixed_time);
108 }
109 compound.insert("has_skylight", self.has_skylight);
110 compound.insert("has_ceiling", self.has_ceiling);
111 compound.insert("coordinate_scale", self.coordinate_scale);
112 compound.insert("min_y", self.min_y);
113 compound.insert("height", self.height);
114 compound.insert("logical_height", self.logical_height);
115 compound.insert("infiniburn", self.infiniburn);
116 compound.insert("ambient_light", self.ambient_light);
117 compound.insert("has_ender_dragon_fight", self.has_ender_dragon_fight);
118 if let Some(clock) = self.default_clock {
119 compound.insert("default_clock", clock);
120 }
121 if let Some(timelines) = self.timelines {
122 compound.insert("timelines", timelines);
123 }
124 if let Some(skybox) = self.skybox {
125 compound.insert("skybox", skybox);
126 }
127 if let Some(cardinal_light) = self.cardinal_light {
128 compound.insert("cardinal_light", cardinal_light);
129 }
130 compound.insert(
131 "monster_spawn_light_level",
132 match &self.monster_spawn_light_level {
133 MonsterSpawnLightLevel::Simple(v) => NbtTag::Int(*v),
134 MonsterSpawnLightLevel::Complex {
135 distribution_type,
136 min_inclusive,
137 max_inclusive,
138 } => {
139 let mut inner = NbtCompound::new();
140 inner.insert("type", *distribution_type);
141 inner.insert("min_inclusive", *min_inclusive);
142 inner.insert("max_inclusive", *max_inclusive);
143 NbtTag::Compound(inner)
144 }
145 },
146 );
147 compound.insert(
148 "monster_spawn_block_light_limit",
149 self.monster_spawn_block_light_limit,
150 );
151
152 let mut attributes = NbtCompound::new();
154
155 if let Some(sky_color) = self.sky_color {
157 attributes.insert("minecraft:visual/sky_color", sky_color);
158 }
159 if let Some(fog_color) = self.fog_color {
160 attributes.insert("minecraft:visual/fog_color", fog_color);
161 }
162 if let Some(cloud_color) = self.cloud_color {
163 attributes.insert("minecraft:visual/cloud_color", cloud_color);
164 }
165 if let Some(cloud_height) = self.cloud_height {
166 attributes.insert("minecraft:visual/cloud_height", cloud_height);
167 }
168 if let Some(ambient_light_color) = self.ambient_light_color {
169 attributes.insert("minecraft:visual/ambient_light_color", ambient_light_color);
170 }
171 if let Some(sky_light_color) = self.sky_light_color {
172 attributes.insert("minecraft:visual/sky_light_color", sky_light_color);
173 }
174 if let Some(sky_light_factor) = self.sky_light_factor {
175 attributes.insert("minecraft:visual/sky_light_factor", sky_light_factor);
176 }
177 if let Some(fog_start_distance) = self.fog_start_distance {
178 attributes.insert("minecraft:visual/fog_start_distance", fog_start_distance);
179 }
180 if let Some(fog_end_distance) = self.fog_end_distance {
181 attributes.insert("minecraft:visual/fog_end_distance", fog_end_distance);
182 }
183 if let Some(particle_type) = self.default_dripstone_particle {
184 let mut particle = NbtCompound::new();
185 particle.insert("type", particle_type);
186 attributes.insert(
187 "minecraft:visual/default_dripstone_particle",
188 NbtTag::Compound(particle),
189 );
190 }
191
192 attributes.insert(
194 "minecraft:gameplay/respawn_anchor_works",
195 self.respawn_anchor_works,
196 );
197 attributes.insert("minecraft:gameplay/can_start_raid", self.can_start_raid);
198 if self.fast_lava {
199 attributes.insert("minecraft:gameplay/fast_lava", self.fast_lava);
200 }
201 if !self.piglins_zombify {
202 attributes.insert("minecraft:gameplay/piglins_zombify", self.piglins_zombify);
203 }
204 if let Some(sky_light_level) = self.sky_light_level {
205 attributes.insert("minecraft:gameplay/sky_light_level", sky_light_level);
206 }
207 if self.snow_golem_melts {
208 attributes.insert("minecraft:gameplay/snow_golem_melts", self.snow_golem_melts);
209 }
210 if self.water_evaporates {
211 attributes.insert("minecraft:gameplay/water_evaporates", self.water_evaporates);
212 }
213 if self.nether_portal_spawns_piglin {
214 attributes.insert(
215 "minecraft:gameplay/nether_portal_spawns_piglin",
216 self.nether_portal_spawns_piglin,
217 );
218 }
219
220 {
222 let mut bed_rule = NbtCompound::new();
223 bed_rule.insert("can_set_spawn", self.bed_rule.can_set_spawn);
224 bed_rule.insert("can_sleep", self.bed_rule.can_sleep);
225 if self.bed_rule.explodes {
226 bed_rule.insert("explodes", self.bed_rule.explodes);
227 }
228 if let Some(key) = self.bed_rule.error_message_key {
229 let mut msg = NbtCompound::new();
230 msg.insert("translate", key);
231 bed_rule.insert("error_message", NbtTag::Compound(msg));
232 }
233 attributes.insert("minecraft:gameplay/bed_rule", NbtTag::Compound(bed_rule));
234 }
235
236 if let Some(mood) = &self.mood_sound {
238 let mut mood_compound = NbtCompound::new();
239 let sound = mood.sound.key.to_string();
240 mood_compound.insert("sound", sound.as_str());
241 mood_compound.insert("tick_delay", mood.tick_delay);
242 mood_compound.insert("block_search_extent", mood.block_search_extent);
243 mood_compound.insert("offset", mood.offset);
244 let mut ambient_sounds = NbtCompound::new();
245 ambient_sounds.insert("mood", NbtTag::Compound(mood_compound));
246 attributes.insert(
247 "minecraft:audio/ambient_sounds",
248 NbtTag::Compound(ambient_sounds),
249 );
250 }
251 if let Some(bg_music) = &self.background_music {
252 let mut music_compound = NbtCompound::new();
253 let mut default_entry = NbtCompound::new();
254 let sound = bg_music.default.sound.key.to_string();
255 default_entry.insert("sound", sound.as_str());
256 default_entry.insert("min_delay", bg_music.default.min_delay);
257 default_entry.insert("max_delay", bg_music.default.max_delay);
258 if bg_music.default.replace_current_music {
259 default_entry.insert(
260 "replace_current_music",
261 bg_music.default.replace_current_music,
262 );
263 }
264 music_compound.insert("default", NbtTag::Compound(default_entry));
265 if let Some(creative) = &bg_music.creative {
266 let mut creative_entry = NbtCompound::new();
267 let sound = creative.sound.key.to_string();
268 creative_entry.insert("sound", sound.as_str());
269 creative_entry.insert("min_delay", creative.min_delay);
270 creative_entry.insert("max_delay", creative.max_delay);
271 if creative.replace_current_music {
272 creative_entry.insert("replace_current_music", creative.replace_current_music);
273 }
274 music_compound.insert("creative", NbtTag::Compound(creative_entry));
275 }
276 attributes.insert(
277 "minecraft:audio/background_music",
278 NbtTag::Compound(music_compound),
279 );
280 }
281
282 if !attributes.is_empty() {
283 compound.insert("attributes", NbtTag::Compound(attributes));
284 }
285
286 NbtTag::Compound(compound)
287 }
288}
289
290pub type DimensionTypeRef = &'static DimensionType;
291
292impl PartialEq for DimensionTypeRef {
293 #[expect(clippy::disallowed_methods)] fn eq(&self, other: &Self) -> bool {
295 std::ptr::eq(*self, *other)
296 }
297}
298
299impl Eq for DimensionTypeRef {}
300
301pub struct DimensionTypeRegistry {
302 dimension_types_by_id: Vec<DimensionTypeRef>,
303 dimension_types_by_key: FxHashMap<Identifier, usize>,
304 allows_registering: bool,
305}
306
307impl DimensionTypeRegistry {
308 #[must_use]
309 pub fn new() -> Self {
310 Self {
311 dimension_types_by_id: Vec::new(),
312 dimension_types_by_key: FxHashMap::default(),
313 allows_registering: true,
314 }
315 }
316
317 #[must_use]
318 pub fn get_ids(&self) -> Vec<Identifier> {
319 self.dimension_types_by_key.keys().cloned().collect()
320 }
321}
322
323crate::impl_standard_methods!(
324 DimensionTypeRegistry,
325 DimensionTypeRef,
326 dimension_types_by_id,
327 dimension_types_by_key,
328 allows_registering
329);
330
331crate::impl_registry!(
332 DimensionTypeRegistry,
333 DimensionType,
334 dimension_types_by_id,
335 dimension_types_by_key,
336 dimension_types
337);