steel_core/worldgen/feature/
weather.rs1use super::prelude::*;
2use super::runner::FeatureDecorationRunner;
3
4static TEMPERATURE_NOISE: LazyLock<PerlinSimplexNoise> = LazyLock::new(|| {
5 let mut random = RandomSource::Legacy(LegacyRandom::from_seed(1234));
6 PerlinSimplexNoise::new(&mut random, &[0])
7});
8
9static FROZEN_TEMPERATURE_NOISE: LazyLock<PerlinSimplexNoise> = LazyLock::new(|| {
10 let mut random = RandomSource::Legacy(LegacyRandom::from_seed(3456));
11 PerlinSimplexNoise::new(&mut random, &[-2, -1, 0])
12});
13
14static BIOME_INFO_NOISE: LazyLock<PerlinSimplexNoise> = LazyLock::new(|| {
15 let mut random = RandomSource::Legacy(LegacyRandom::from_seed(2345));
16 PerlinSimplexNoise::new(&mut random, &[0])
17});
18
19impl FeatureDecorationRunner {
20 pub(super) fn biome_info_noise_value(x: f64, z: f64) -> f64 {
21 BIOME_INFO_NOISE.get_value(x, z)
22 }
23
24 pub(super) fn biome_at_block(
25 region: &WorldGenRegion<'_>,
26 registry: &Registry,
27 biome_zoom_seed: i64,
28 pos: BlockPos,
29 ) -> BiomeRef {
30 let biome_id = fuzzed_biome_at_block(biome_zoom_seed, pos, |quart| {
31 region.noise_biome_id(quart.x, quart.y, quart.z)
32 });
33 let Some(biome) = registry.biomes.by_id(usize::from(biome_id)) else {
34 panic!("biome lookup resolved unknown biome id {biome_id}");
35 };
36 biome
37 }
38
39 pub(super) fn should_freeze(
40 region: &WorldGenRegion<'_>,
41 registry: &Registry,
42 biome_zoom_seed: i64,
43 pos: BlockPos,
44 check_neighbors: bool,
45 ) -> bool {
46 let biome = Self::biome_at_block(region, registry, biome_zoom_seed, pos);
47 Self::should_freeze_in_biome(region, biome, pos, check_neighbors)
48 }
49
50 pub(super) fn should_freeze_in_biome(
51 region: &WorldGenRegion<'_>,
52 biome: BiomeRef,
53 pos: BlockPos,
54 check_neighbors: bool,
55 ) -> bool {
56 if Self::warm_enough_to_rain(region, biome, pos)
57 || region.is_outside_build_height(pos.y())
58 || region.block_light_at(pos) >= 10
59 {
60 return false;
61 }
62
63 let state = region.block_state(pos);
64 if state.get_block() != &vanilla_blocks::WATER
65 || !get_fluid_state_from_block(state).is_water()
66 {
67 return false;
68 }
69
70 if !check_neighbors {
71 return true;
72 }
73
74 !(Self::is_water_at(region, pos.west())
75 && Self::is_water_at(region, pos.east())
76 && Self::is_water_at(region, pos.north())
77 && Self::is_water_at(region, pos.south()))
78 }
79
80 pub(super) fn should_snow_in_biome(
81 region: &WorldGenRegion<'_>,
82 biome: BiomeRef,
83 pos: BlockPos,
84 ) -> bool {
85 if !biome.has_precipitation
86 || Self::warm_enough_to_rain(region, biome, pos)
87 || region.is_outside_build_height(pos.y())
88 || region.block_light_at(pos) >= 10
89 {
90 return false;
91 }
92
93 let state = region.block_state(pos);
94 if !state.is_air() && state.get_block() != &vanilla_blocks::SNOW {
95 return false;
96 }
97
98 let snow = vanilla_blocks::SNOW.default_state();
99 BLOCK_BEHAVIORS
100 .get_behavior(snow.get_block())
101 .can_survive(snow, region, pos)
102 }
103
104 fn is_water_at(region: &WorldGenRegion<'_>, pos: BlockPos) -> bool {
105 get_fluid_state_from_block(region.block_state(pos)).is_water()
106 }
107
108 fn warm_enough_to_rain(region: &WorldGenRegion<'_>, biome: BiomeRef, pos: BlockPos) -> bool {
109 Self::biome_temperature(region, biome, pos) >= 0.15
110 }
111
112 fn biome_temperature(region: &WorldGenRegion<'_>, biome: BiomeRef, pos: BlockPos) -> f32 {
113 let base_temp = biome.temperature;
114 let modified_temp = match biome.temperature_modifier {
115 TemperatureModifier::None => base_temp,
116 TemperatureModifier::Frozen => {
117 let large = FROZEN_TEMPERATURE_NOISE
118 .get_value(f64::from(pos.x()) * 0.05, f64::from(pos.z()) * 0.05)
119 * 7.0;
120 let edge =
121 BIOME_INFO_NOISE.get_value(f64::from(pos.x()) * 0.2, f64::from(pos.z()) * 0.2);
122 if large + edge < 0.3 {
123 let small = BIOME_INFO_NOISE
124 .get_value(f64::from(pos.x()) * 0.09, f64::from(pos.z()) * 0.09);
125 if small < 0.8 {
126 return 0.2;
127 }
128 }
129 base_temp
130 }
131 };
132
133 let snow_level = region.sea_level() + 17;
134 if pos.y() <= snow_level {
135 return modified_temp;
136 }
137
138 let value = TEMPERATURE_NOISE.get_value(f64::from(pos.x()) / 8.0, f64::from(pos.z()) / 8.0)
139 as f32
140 * 8.0;
141 modified_temp - (value + pos.y() as f32 - snow_level as f32) * 0.05 / 40.0
142 }
143}