diff --git a/Content.Server/Worldgen/Prototypes/BiomePrototype.cs b/Content.Server/Worldgen/Prototypes/BiomePrototype.cs
index e1316ee69d9..570d85f712c 100644
--- a/Content.Server/Worldgen/Prototypes/BiomePrototype.cs
+++ b/Content.Server/Worldgen/Prototypes/BiomePrototype.cs
@@ -13,7 +13,7 @@ namespace Content.Server.Worldgen.Prototypes;
public sealed partial class BiomePrototype : IPrototype, IInheritingPrototype
{
///
- [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))]
+ [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))] // Frontier: EntityPrototype
@@ -25,6 +25,36 @@ public sealed partial class BiomePrototype : IPrototype, IInheritingPrototype
[IdDataField]
public string ID { get; private set; } = default!;
+ // Frontier: distances
+ ///
+ /// The valid range of biome coordinate lengths (3000, 4000) => 5000
+ /// Chunks with center points within this range may be generated with this biome.
+ ///
+ [ViewVariables]
+ private Vector2? _distanceRange;
+
+ ///
+ /// Accessor for range
+ ///
+ [DataField]
+ public Vector2? DistanceRange
+ {
+ get { return _distanceRange; }
+ private set
+ {
+ _distanceRange = value;
+
+ if (value == null)
+ DistanceRangeSquared = null;
+ else
+ DistanceRangeSquared = value * value;
+ }
+ }
+
+ [ViewVariables]
+ public Vector2? DistanceRangeSquared { get; private set; }
+ // Frontier: distances
+
///
/// The valid ranges of noise values under which this biome can be picked.
///
diff --git a/Content.Server/Worldgen/Systems/Biomes/BiomeSelectionSystem.cs b/Content.Server/Worldgen/Systems/Biomes/BiomeSelectionSystem.cs
index dd7ac9d62b2..78d342dcd5d 100644
--- a/Content.Server/Worldgen/Systems/Biomes/BiomeSelectionSystem.cs
+++ b/Content.Server/Worldgen/Systems/Biomes/BiomeSelectionSystem.cs
@@ -25,9 +25,17 @@ public override void Initialize()
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;
@@ -45,6 +53,17 @@ private void OnBiomeSelectionStartup(EntityUid uid, BiomeSelectionComponent comp
.Select(x => x.Id)
.ToList();
+ // 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) =>
(biome.MinX is null || biome.MaxX is null || biome.MinY is null || biome.MaxY is null)
? CheckNoiseRanges(chunk, biome, coords) : CheckSpecificChunkRange(biome, coords);
diff --git a/Content.Server/Worldgen/Systems/Carvers/NoiseRangeCarverSystem.cs b/Content.Server/Worldgen/Systems/Carvers/NoiseRangeCarverSystem.cs
index f2e051669a2..d39a8677ef0 100644
--- a/Content.Server/Worldgen/Systems/Carvers/NoiseRangeCarverSystem.cs
+++ b/Content.Server/Worldgen/Systems/Carvers/NoiseRangeCarverSystem.cs
@@ -20,6 +20,11 @@ public override void Initialize()
private void OnPrePlaceDebris(EntityUid uid, NoiseRangeCarverComponent component,
ref PrePlaceDebrisFeatureEvent args)
{
+ // Frontier: something handled this, nothing to do
+ if (args.Handled)
+ return;
+ // End Frontier
+
var coords = WorldGen.WorldToChunkCoords(args.Coords.ToMapPos(EntityManager, _transform));
var val = _index.Evaluate(uid, component.NoiseChannel, coords);
diff --git a/Content.Server/_NF/Worldgen/Component/Carvers/PointSetDistanceCarverComponent.cs b/Content.Server/_NF/Worldgen/Component/Carvers/PointSetDistanceCarverComponent.cs
new file mode 100644
index 00000000000..934492c8db5
--- /dev/null
+++ b/Content.Server/_NF/Worldgen/Component/Carvers/PointSetDistanceCarverComponent.cs
@@ -0,0 +1,7 @@
+namespace Content.Server._NF.Worldgen.Components.Carvers;
+
+///
+/// This prevents world generation objects from being spawned too close to items of importance.
+///
+[RegisterComponent]
+public sealed partial class PointSetDistanceCarverComponent : Component;
\ No newline at end of file
diff --git a/Content.Server/_NF/Worldgen/Component/Carvers/WorldGenDistanceCarverComponent.cs b/Content.Server/_NF/Worldgen/Component/Carvers/WorldGenDistanceCarverComponent.cs
new file mode 100644
index 00000000000..a29aad32540
--- /dev/null
+++ b/Content.Server/_NF/Worldgen/Component/Carvers/WorldGenDistanceCarverComponent.cs
@@ -0,0 +1,38 @@
+namespace Content.Server._NF.Worldgen.Components.Carvers;
+
+///
+/// This denotes an entity that spawns fewer asteroids around it.
+///
+[RegisterComponent]
+public sealed partial class WorldGenDistanceCarverComponent : Component
+{
+ ///
+ /// The probability that something within a given distance is generated.
+ /// No need to be ordered.
+ ///
+ [DataField]
+ public List DistanceThresholds = new();
+
+ ///
+ /// The probability that something within a given squared distance is generated.
+ /// For internal use, _must_ be ordered in descending order of distance.
+ ///
+ [ViewVariables]
+ public List SquaredDistanceThresholds = new();
+}
+
+[DataDefinition]
+public sealed partial class WorldGenDistanceThreshold
+{
+ ///
+ /// The maximum distance within the threshold.
+ ///
+ [DataField]
+ public float MaxDistance;
+
+ ///
+ /// The probability that something within this distance will spawn.
+ ///
+ [DataField]
+ public float Prob = 1.0f;
+}
\ No newline at end of file
diff --git a/Content.Server/_NF/Worldgen/Systems/Carvers/DistanceProbabilityFilterSystem.cs b/Content.Server/_NF/Worldgen/Systems/Carvers/DistanceProbabilityFilterSystem.cs
new file mode 100644
index 00000000000..e6badf0bfc5
--- /dev/null
+++ b/Content.Server/_NF/Worldgen/Systems/Carvers/DistanceProbabilityFilterSystem.cs
@@ -0,0 +1,64 @@
+using System.Linq;
+using System.Numerics;
+using Content.Server._NF.Worldgen.Components.Carvers;
+using Content.Server.Worldgen.Systems.Debris;
+using Robust.Shared.Random;
+
+namespace Content.Server._NF.Worldgen.Systems.Carvers;
+
+///
+/// This carves out holes in world gen based on distance from a set of known points.
+///
+public sealed class PointSetDistanceCarverSystem : EntitySystem
+{
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
+ [Dependency] private readonly IRobustRandom _random = default!;
+ // Cache points for lookup
+
+ ///
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(OnInit);
+ SubscribeLocalEvent(OnPrePlaceDebris);
+ }
+
+ private void OnInit(Entity ent,
+ ref ComponentInit args)
+ {
+ ent.Comp.SquaredDistanceThresholds = ent.Comp.DistanceThresholds
+ .OrderByDescending(x => x.MaxDistance)
+ .Select(x => new WorldGenDistanceThreshold { MaxDistance = x.MaxDistance * x.MaxDistance, Prob = x.Prob })
+ .ToList();
+ }
+
+ private void OnPrePlaceDebris(EntityUid uid, PointSetDistanceCarverComponent component,
+ ref PrePlaceDebrisFeatureEvent args)
+ {
+ // Frontier: something handled this, nothing to do
+ if (args.Handled)
+ return;
+ // End Frontier
+
+ var coords = _transform.ToMapCoordinates(args.Coords);
+
+ var prob = 1.0f;
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out _, out var carver, out var xform))
+ {
+ var distanceSquared = Vector2.DistanceSquared(_transform.ToMapCoordinates(xform.Coordinates).Position, coords.Position);
+ float? newProb = null;
+ foreach (var threshold in carver.SquaredDistanceThresholds)
+ {
+ if (distanceSquared > threshold.MaxDistance)
+ break;
+
+ newProb = threshold.Prob;
+ }
+ if (newProb != null)
+ prob = float.Min(prob, newProb.Value);
+ }
+
+ if (!_random.Prob(prob))
+ args.Handled = true;
+ }
+}
\ No newline at end of file