Skip to main content

steel_core/worldgen/feature/
weather.rs

1use 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}