using System.Linq;
using Content.Server.Worldgen.Components;
using Content.Server.Worldgen.Prototypes;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager;
namespace Content.Server.Worldgen.Systems.Biomes;
///
/// This handles biome selection, evaluating which biome to apply to a chunk based on noise channels.
///
public sealed class BiomeSelectionSystem : BaseWorldSystem
{
[Dependency] private readonly NoiseIndexSystem _noiseIdx = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly ISerializationManager _ser = default!;
///
public override void Initialize()
{
SubscribeLocalEvent(OnBiomeSelectionStartup);
SubscribeLocalEvent(OnWorldChunkAdded);
}
private void OnWorldChunkAdded(EntityUid uid, BiomeSelectionComponent component, ref WorldChunkAddedEvent args)
{
var coords = args.Coords;
var lengthSquared = WorldGen.ChunkToWorldCoordsCentered(coords).LengthSquared(); // Frontier: cache world coords of center of chunk
foreach (var biomeId in component.Biomes)
{
var biome = _proto.Index(biomeId);
// Frontier: check range
if (!CheckBiomeRange(biome, lengthSquared))
continue;
// End Frontier
if (!CheckBiomeValidity(args.Chunk, biome, coords))
continue;
biome.Apply(args.Chunk, _ser, EntityManager);
return;
}
Log.Error($"Biome selection ran out of biomes to select? See biomes list: {component.Biomes}");
}
private void OnBiomeSelectionStartup(EntityUid uid, BiomeSelectionComponent component, ComponentStartup args)
{
// surely this can't be THAAAAAAAAAAAAAAAT bad right????
var sorted = component.Biomes
.Select(x => (Id: x, _proto.Index(x).Priority))
.OrderByDescending(x => x.Priority)
.Select(x => x.Id)
.ToList();
component.Biomes = sorted; // my hopes and dreams rely on this being pre-sorted by priority.
}
// Frontier: check that a given point (passed as the square of its length) meets the range requirements of a biome
private bool CheckBiomeRange(BiomePrototype biome, float centerLengthSquared)
{
if (biome.DistanceRangeSquared == null)
return true;
return centerLengthSquared >= biome.DistanceRangeSquared.Value.X
&& centerLengthSquared <= biome.DistanceRangeSquared.Value.Y;
}
// End Frontier
private bool CheckBiomeValidity(EntityUid chunk, BiomePrototype biome, Vector2i coords)
{
foreach (var (noise, ranges) in biome.NoiseRanges)
{
var value = _noiseIdx.Evaluate(chunk, noise, coords);
var anyValid = false;
foreach (var range in ranges)
{
if (range.X < value && value < range.Y)
{
anyValid = true;
break;
}
}
if (!anyValid)
return false;
}
return true;
}
}