using HSchool.Content; using HSchool.People; namespace HSchool.Simulation; /// /// Outdoor temperature and precipitation from a climate preset, the school seed, and the /// calendar. Same inputs always produce the same street. Not a tick accumulator — sample it /// when the clock label or warmth would move. /// public static class WeatherSampler { private const int DaySalt = 0x57EA11; private const int HourSalt = 0x57EA12; public static OutdoorWeather Sample(ClimatePresetDef preset, int schoolSeed, DateTime time) { ArgumentNullException.ThrowIfNull(preset); var utc = DateTime.SpecifyKind(time, DateTimeKind.Utc); var month = utc.Month; var norm = MonthNorm(preset, month); var dayNumber = DateOnly.FromDateTime(utc).DayNumber; var dayNoise = SignedUnit(Seed.Mix(schoolSeed, dayNumber, DaySalt)) * preset.DaySpread; var hour = utc.Hour + utc.Minute / 60d + utc.Second / 3600d; var diurnal = -Math.Cos((hour - 3d) / 24d * 2d * Math.PI) * preset.HourSpread; var temperature = (float)(norm + dayNoise + diurnal); var wet = Unit(Seed.Mix(schoolSeed, dayNumber * 24 + utc.Hour, HourSalt)) < preset.PrecipitationChance; var precipitation = !wet ? Precipitation.None : temperature < 0f ? Precipitation.Snow : Precipitation.Rain; return new OutdoorWeather(temperature, precipitation); } private static float MonthNorm(ClimatePresetDef preset, int month) { if (preset.MonthlyNorms.Count != 12) { return 0f; } return preset.MonthlyNorms[month - 1]; } private static double Unit(int mixed) { return (uint)mixed / (double)uint.MaxValue; } private static double SignedUnit(int mixed) => Unit(mixed) * 2d - 1d; }