diff --git a/RandomWeatherPlugin/RandomWeather.cs b/RandomWeatherPlugin/RandomWeather.cs index 9d4e31bd..0753f840 100644 --- a/RandomWeatherPlugin/RandomWeather.cs +++ b/RandomWeatherPlugin/RandomWeather.cs @@ -27,9 +27,6 @@ public RandomWeather(RandomWeatherConfiguration configuration, WeatherManager we if (_configuration.Mode == RandomWeatherMode.TransitionTable) { - if (_configuration.WeatherTransitions.Count == 0) - throw new ConfigurationException("No entries were found in the WeatherTransitions list"); - var next = _weatherTypeProvider.GetWeatherType(_configuration.WeatherTransitions.First().Key); var last = _weatherManager.CurrentWeather; _weatherManager.SetWeather(new WeatherData(last.Type, next) @@ -47,22 +44,19 @@ public RandomWeather(RandomWeatherConfiguration configuration, WeatherManager we RainWater = last.RainWater, TrackGrip = last.TrackGrip }); - + RecalculateWeights(_configuration.WeatherTransitions[next.WeatherFxType]); } else if (_configuration.Mode == RandomWeatherMode.Default) { - if (_configuration.WeatherWeights.Count == 0) - throw new ConfigurationException("No entries were found in the WeatherWeights list"); - - _configuration.WeatherWeights[WeatherFxType.None] = 0; - RecalculateWeights(_configuration.WeatherWeights); } } - private void RecalculateWeights(Dictionary input) + private void RecalculateWeights(Dictionary input) { + _weathers.Clear(); + float weightSum = input .Select(w => w.Value) .Sum(); @@ -70,7 +64,7 @@ private void RecalculateWeights(Dictionary input) float prefixSum = 0.0f; foreach (var (weather, weight) in input) { - if (weight > 0) + if (weight > 0 && weather != WeatherFxType.None) { prefixSum += weight / weightSum; _weathers.Add(new WeatherWeight @@ -119,13 +113,15 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) { int weatherDuration = 1000; int transitionDuration = 1000; + bool firstRun = true; while (!stoppingToken.IsCancellationRequested) { try { weatherDuration = Random.Shared.Next(_configuration.MinWeatherDurationMilliseconds, _configuration.MaxWeatherDurationMilliseconds); - transitionDuration = Random.Shared.Next(_configuration.MinTransitionDurationMilliseconds, _configuration.MaxTransitionDurationMilliseconds); + transitionDuration = (firstRun && _configuration.RandomizeInitialWeather) ? 0 : Random.Shared.Next(_configuration.MinTransitionDurationMilliseconds, _configuration.MaxTransitionDurationMilliseconds); + firstRun = false; var next = PickRandom(); var nextWeatherType = _weatherTypeProvider.GetWeatherType(next); @@ -136,7 +132,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) nextWeatherType.WeatherFxType, Math.Round(transitionDuration / 1000.0f), Math.Round(weatherDuration / 60_000.0f, 1)); - + _weatherManager.SetWeather(new WeatherData(last.Type, nextWeatherType) { TransitionDuration = transitionDuration, @@ -152,7 +148,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) RainWater = last.RainWater, TrackGrip = last.TrackGrip }); - + if (_configuration.Mode == RandomWeatherMode.TransitionTable) RecalculateWeights(_configuration.WeatherTransitions[next]); } diff --git a/RandomWeatherPlugin/RandomWeatherConfiguration.cs b/RandomWeatherPlugin/RandomWeatherConfiguration.cs index b4025d3e..c5bbab7a 100644 --- a/RandomWeatherPlugin/RandomWeatherConfiguration.cs +++ b/RandomWeatherPlugin/RandomWeatherConfiguration.cs @@ -21,6 +21,9 @@ public class RandomWeatherConfiguration : IValidateConfiguration> WeatherTransitions { get; init; } = new(); [YamlMember(Description = "Weights for random weather selection, removing a weight or setting it to 0 blacklists a weather\nYou can also use decimals like 0.1")] diff --git a/RandomWeatherPlugin/RandomWeatherConfigurationValidator.cs b/RandomWeatherPlugin/RandomWeatherConfigurationValidator.cs index c74c501a..fd15c159 100644 --- a/RandomWeatherPlugin/RandomWeatherConfigurationValidator.cs +++ b/RandomWeatherPlugin/RandomWeatherConfigurationValidator.cs @@ -1,4 +1,5 @@ using FluentValidation; +using AssettoServer.Shared.Weather; namespace RandomWeatherPlugin; @@ -6,11 +7,46 @@ public class RandomWeatherConfigurationValidator : AbstractValidator cfg.WeatherWeights).ChildRules(ww => - { - ww.RuleFor(w => w.Value).GreaterThanOrEqualTo(0); - }); RuleFor(cfg => cfg.MinWeatherDurationMinutes).LessThanOrEqualTo(cfg => cfg.MaxWeatherDurationMinutes); RuleFor(cfg => cfg.MinTransitionDurationSeconds).LessThanOrEqualTo(cfg => cfg.MaxTransitionDurationSeconds); + When(cfg => cfg.Mode == RandomWeatherMode.TransitionTable, () => + { + RuleFor(cfg => cfg.WeatherTransitions) + .NotEmpty() + .DependentRules(() => + { + RuleFor(cfg => cfg.WeatherTransitions) + .Must(wt => !wt.ContainsKey(WeatherFxType.None)) + .WithMessage("WeatherFX Type \"None\" cannot be used as a weather"); + RuleForEach(cfg => cfg.WeatherTransitions) + .Must(x => !x.Value.ContainsKey(WeatherFxType.None) || x.Value[WeatherFxType.None] <= 0) + .WithMessage("WeatherFX Type \"None\" cannot be used as a weather"); + RuleForEach(cfg => cfg.WeatherTransitions) + .Must(wt => wt.Value.Values.Any(v => v > 0)) + .WithMessage("Every WeatherTransitions entry must contain at least one destination weather with a weight greater than 0"); + RuleFor(cfg => cfg.WeatherTransitions) + .Must(wt => + { + var destinations = wt.Values.SelectMany(d => d.Keys).Distinct(); + return destinations.All(d => d == WeatherFxType.None || wt.ContainsKey(d)); + }).WithMessage("Every weather that can be transitioned to must also have its own WeatherTransitions section"); + }); + }); + When(cfg => cfg.Mode == RandomWeatherMode.Default, () => + { + RuleFor(cfg => cfg.WeatherWeights) + .NotEmpty() + .DependentRules(() => + { + RuleFor(cfg => cfg.WeatherWeights) + .Must(ww => !ww.ContainsKey(WeatherFxType.None) || ww[WeatherFxType.None] <= 0) + .WithMessage("WeatherFX Type \"None\" cannot be used as a weather"); + RuleFor(cfg => cfg.WeatherWeights) + .Must(ww => ww.Values.Any(v => v > 0)) + .WithMessage("At least one entry in WeatherWeights must have a weight greater than 0"); + RuleForEach(cfg => cfg.WeatherWeights) + .ChildRules(ww => { ww.RuleFor(w => w.Value).GreaterThanOrEqualTo(0); }); + }); + }); } }