Add deterministic weather fallback
This commit is contained in:
@@ -24,7 +24,7 @@ bool UAgrarianWeatherProviderSubsystem::RequestWeatherForActiveGameState()
|
||||
|
||||
bool UAgrarianWeatherProviderSubsystem::RequestWeatherForTile(FName TileId, float Latitude, float Longitude)
|
||||
{
|
||||
if (!bEnableLiveWeatherRequests || TileId == NAME_None)
|
||||
if (TileId == NAME_None)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -40,6 +40,10 @@ bool UAgrarianWeatherProviderSubsystem::RequestWeatherForTile(FName TileId, floa
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!bEnableLiveWeatherRequests)
|
||||
{
|
||||
return ApplyDeterministicFallbackWeather(TileId, Latitude, Longitude, GameState);
|
||||
}
|
||||
|
||||
const FString Url = BuildOpenMeteoForecastUrl(TileId, Latitude, Longitude);
|
||||
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = FHttpModule::Get().CreateRequest();
|
||||
@@ -163,6 +167,57 @@ void UAgrarianWeatherProviderSubsystem::ClearWeatherSnapshotCache()
|
||||
ServerWeatherSnapshotCache.Empty();
|
||||
}
|
||||
|
||||
bool UAgrarianWeatherProviderSubsystem::ApplyDeterministicFallbackWeather(FName TileId, float Latitude, float Longitude, AAgrarianGameState* GameState)
|
||||
{
|
||||
if (!bEnableDeterministicFallbackWeather || !GameState || !GameState->HasAuthority() || TileId == NAME_None)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const FAgrarianWeatherProviderSnapshot Snapshot = BuildDeterministicFallbackSnapshot(
|
||||
TileId,
|
||||
Latitude,
|
||||
Longitude,
|
||||
GameState->ActiveDayOfYear,
|
||||
GameState->WorldHours);
|
||||
LastSnapshot = Snapshot;
|
||||
CacheSnapshot(Snapshot, WeatherSnapshotCacheTtlSeconds);
|
||||
return ApplySnapshotToGameState(Snapshot, GameState);
|
||||
}
|
||||
|
||||
FAgrarianWeatherProviderSnapshot UAgrarianWeatherProviderSubsystem::BuildDeterministicFallbackSnapshot(FName TileId, float Latitude, float Longitude, int32 DayOfYear, float HourOfDay) const
|
||||
{
|
||||
const int32 SafeDay = FMath::Clamp(DayOfYear, 1, 366);
|
||||
const float SeasonalRadians = ((static_cast<float>(SafeDay) - 172.0f) / 366.0f) * 2.0f * PI;
|
||||
const float LatitudeSeasonScale = FMath::Clamp(FMath::Abs(Latitude) / 60.0f, 0.0f, 1.0f);
|
||||
const float SeasonalTemperatureC = 12.0f + (FMath::Cos(SeasonalRadians) * 10.0f * LatitudeSeasonScale);
|
||||
const float DailySwingC = FMath::Lerp(4.0f, 10.0f, LatitudeSeasonScale);
|
||||
const float Noise = GetDeterministicWeatherNoise(TileId, SafeDay, 11);
|
||||
const float StormNoise = GetDeterministicWeatherNoise(TileId, SafeDay, 23);
|
||||
const float CloudNoise = GetDeterministicWeatherNoise(TileId, SafeDay, 37);
|
||||
const float WindNoise = GetDeterministicWeatherNoise(TileId, SafeDay, 53);
|
||||
|
||||
FAgrarianWeatherProviderSnapshot Snapshot;
|
||||
Snapshot.TileId = TileId;
|
||||
Snapshot.Latitude = FMath::Clamp(Latitude, -90.0f, 90.0f);
|
||||
Snapshot.Longitude = FMath::Clamp(Longitude, -180.0f, 180.0f);
|
||||
Snapshot.Provider = TEXT("deterministic-fallback");
|
||||
Snapshot.ProviderTimestamp = FString::Printf(TEXT("day-%03d-hour-%02d"), SafeDay, FMath::FloorToInt(FMath::Clamp(HourOfDay, 0.0f, 24.0f)));
|
||||
Snapshot.DailyLowTemperatureC = SeasonalTemperatureC - DailySwingC + FMath::Lerp(-2.0f, 2.0f, Noise);
|
||||
Snapshot.DailyHighTemperatureC = SeasonalTemperatureC + DailySwingC + FMath::Lerp(-2.0f, 2.0f, Noise);
|
||||
Snapshot.CurrentTemperatureC = (Snapshot.DailyLowTemperatureC + Snapshot.DailyHighTemperatureC) * 0.5f;
|
||||
Snapshot.CloudCoverPercent = FMath::Clamp(CloudNoise * 100.0f, 0.0f, 100.0f);
|
||||
Snapshot.RelativeHumidityPercent = FMath::Clamp(45.0f + (Snapshot.CloudCoverPercent * 0.35f) + FMath::Lerp(-10.0f, 10.0f, Noise), 10.0f, 100.0f);
|
||||
Snapshot.WindSpeedKmh = FMath::Clamp(5.0f + (WindNoise * 35.0f), 0.0f, 75.0f);
|
||||
Snapshot.PressureMslHpa = FMath::Clamp(1018.0f - (Snapshot.CloudCoverPercent * 0.12f) - (Snapshot.WindSpeedKmh * 0.08f), 960.0f, 1040.0f);
|
||||
Snapshot.PrecipitationMm = StormNoise > 0.72f ? FMath::Lerp(0.1f, 8.0f, StormNoise) : 0.0f;
|
||||
Snapshot.VisibilityMeters = FMath::Clamp(10000.0f - (Snapshot.CloudCoverPercent * 30.0f) - (Snapshot.PrecipitationMm * 300.0f), 250.0f, 10000.0f);
|
||||
Snapshot.WeatherCode = Snapshot.PrecipitationMm > 0.0f ? 61 : 0;
|
||||
Snapshot.MappedWeather = MapOpenMeteoWeatherCode(Snapshot.WeatherCode, Snapshot.PrecipitationMm, Snapshot.WindSpeedKmh);
|
||||
Snapshot.bIsValid = true;
|
||||
return Snapshot;
|
||||
}
|
||||
|
||||
EAgrarianWeatherType UAgrarianWeatherProviderSubsystem::MapOpenMeteoWeatherCode(int32 WeatherCode, float PrecipitationMm, float WindSpeedKmh)
|
||||
{
|
||||
if (WeatherCode >= 95 || WindSpeedKmh >= 55.0f)
|
||||
@@ -184,6 +239,12 @@ EAgrarianWeatherType UAgrarianWeatherProviderSubsystem::MapOpenMeteoWeatherCode(
|
||||
return EAgrarianWeatherType::Clear;
|
||||
}
|
||||
|
||||
float UAgrarianWeatherProviderSubsystem::GetDeterministicWeatherNoise(FName TileId, int32 DayOfYear, int32 Salt) const
|
||||
{
|
||||
const FString SeedString = FString::Printf(TEXT("%s:%d:%d"), *TileId.ToString(), DayOfYear, Salt);
|
||||
return static_cast<float>(GetTypeHash(SeedString) % 10000) / 9999.0f;
|
||||
}
|
||||
|
||||
FString UAgrarianWeatherProviderSubsystem::MakeCacheKey(FName TileId, const FString& Provider) const
|
||||
{
|
||||
return FString::Printf(TEXT("%s:%s"), *Provider.ToLower(), *TileId.ToString());
|
||||
@@ -207,12 +268,16 @@ void UAgrarianWeatherProviderSubsystem::OnOpenMeteoResponse(FHttpRequestPtr Requ
|
||||
{
|
||||
if (!bWasSuccessful || !Response.IsValid() || Response->GetResponseCode() < 200 || Response->GetResponseCode() >= 300)
|
||||
{
|
||||
UWorld* World = GetWorld();
|
||||
ApplyDeterministicFallbackWeather(TileId, Latitude, Longitude, World ? World->GetGameState<AAgrarianGameState>() : nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
FAgrarianWeatherProviderSnapshot Snapshot;
|
||||
if (!ParseOpenMeteoForecast(Response->GetContentAsString(), TileId, Latitude, Longitude, Snapshot))
|
||||
{
|
||||
UWorld* World = GetWorld();
|
||||
ApplyDeterministicFallbackWeather(TileId, Latitude, Longitude, World ? World->GetGameState<AAgrarianGameState>() : nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -265,12 +330,16 @@ void UAgrarianWeatherProviderSubsystem::OnNoaaNwsGridDataResponse(FHttpRequestPt
|
||||
{
|
||||
if (!bWasSuccessful || !Response.IsValid() || Response->GetResponseCode() < 200 || Response->GetResponseCode() >= 300)
|
||||
{
|
||||
UWorld* World = GetWorld();
|
||||
ApplyDeterministicFallbackWeather(TileId, Latitude, Longitude, World ? World->GetGameState<AAgrarianGameState>() : nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
FAgrarianWeatherProviderSnapshot Snapshot;
|
||||
if (!ParseNoaaNwsGridData(Response->GetContentAsString(), TileId, Latitude, Longitude, Snapshot))
|
||||
{
|
||||
UWorld* World = GetWorld();
|
||||
ApplyDeterministicFallbackWeather(TileId, Latitude, Longitude, World ? World->GetGameState<AAgrarianGameState>() : nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user