Implement climate and weather features in world simulation; enhance API with weather retrieval and climate selection options, update UI to support climate selection during world creation, and improve weather display in the game interface.

This commit is contained in:
Leonid Pershin
2026-08-16 23:05:11 +03:00
parent 3610ee8051
commit 2a8b7b49b3
36 changed files with 3650 additions and 31 deletions
@@ -6,6 +6,7 @@ using TheLivingWorld.Api.Simulation;
using TheLivingWorld.Api.Storage;
using TheLivingWorld.Core.Contracts;
using TheLivingWorld.Core.Export;
using TheLivingWorld.Core.Simulation;
using TheLivingWorld.Osm;
using TheLivingWorld.Osm.Import;
using TheLivingWorld.Osm.Overpass;
@@ -68,6 +69,55 @@ public sealed class WorldGenerationServiceTests : IDisposable
Assert.Equal(start, stored.Clock.GameTime);
}
[Fact]
public async Task StartAsync_guesses_the_climate_from_the_location_when_none_was_picked()
{
using var service = CreateService(maxConcurrentWorlds: 2);
var summary = await service.StartAsync(new CreateWorldRequest
{
Name = "Yakutsk",
Latitude = 62.0339,
Longitude = 129.7331,
SizeKm = 5,
}, CancellationToken.None);
Assert.Equal(ClimateKind.Siberian, summary.Climate);
Assert.Equal(ClimateKind.Siberian, (await _store.GetSummaryAsync(summary.Id))?.Climate);
}
[Fact]
public async Task StartAsync_keeps_a_climate_that_fights_the_latitude()
{
using var service = CreateService(maxConcurrentWorlds: 2);
// Deliberately absurd: a tropical Yakutsk. An explicit choice always wins over the guess.
var summary = await service.StartAsync(new CreateWorldRequest
{
Name = "Tropical Yakutsk",
Latitude = 62.0339,
Longitude = 129.7331,
SizeKm = 5,
Climate = ClimateKind.Equatorial,
}, CancellationToken.None);
Assert.Equal(ClimateKind.Equatorial, summary.Climate);
}
[Fact]
public async Task StartAsync_rejects_a_climate_that_is_not_in_the_catalogue()
{
using var service = CreateService(maxConcurrentWorlds: 2);
await Assert.ThrowsAsync<ArgumentException>(() => service.StartAsync(new CreateWorldRequest
{
Latitude = 31.8966010,
Longitude = -100.4858591,
SizeKm = 5,
Climate = (ClimateKind)99,
}, CancellationToken.None));
}
private WorldGenerationService CreateService(int maxConcurrentWorlds)
{
// The capacity check runs before any Overpass work, so this generator is never invoked by the