Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion Content.Server/Worldgen/Prototypes/BiomePrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace Content.Server.Worldgen.Prototypes;
public sealed partial class BiomePrototype : IPrototype, IInheritingPrototype
{
/// <inheritdoc />
[ParentDataField(typeof(AbstractPrototypeIdArraySerializer<EntityPrototype>))]
[ParentDataField(typeof(AbstractPrototypeIdArraySerializer<BiomePrototype>))] // Frontier: EntityPrototype<BiomePrototype
public string[]? Parents { get; }

/// <inheritdoc />
Expand All @@ -25,6 +25,36 @@ public sealed partial class BiomePrototype : IPrototype, IInheritingPrototype
[IdDataField]
public string ID { get; private set; } = default!;

// Frontier: distances
/// <summary>
/// The valid range of biome coordinate lengths (3000, 4000) => 5000
/// Chunks with center points within this range may be generated with this biome.
/// </summary>
[ViewVariables]
private Vector2? _distanceRange;

/// <summary>
/// Accessor for range
/// </summary>
[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

/// <summary>
/// The valid ranges of noise values under which this biome can be picked.
/// </summary>
Expand Down
19 changes: 19 additions & 0 deletions Content.Server/Worldgen/Systems/Biomes/BiomeSelectionSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<BiomePrototype>(biomeId);

// Frontier: check range
if (!CheckBiomeRange(biome, lengthSquared))
continue;
// End Frontier

if (!CheckBiomeValidity(args.Chunk, biome, coords))
continue;

Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Content.Server._NF.Worldgen.Components.Carvers;

/// <summary>
/// This prevents world generation objects from being spawned too close to items of importance.
/// </summary>
[RegisterComponent]
public sealed partial class PointSetDistanceCarverComponent : Component;
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
namespace Content.Server._NF.Worldgen.Components.Carvers;

/// <summary>
/// This denotes an entity that spawns fewer asteroids around it.
/// </summary>
[RegisterComponent]
public sealed partial class WorldGenDistanceCarverComponent : Component
{
/// <summary>
/// The probability that something within a given distance is generated.
/// No need to be ordered.
/// </summary>
[DataField]
public List<WorldGenDistanceThreshold> DistanceThresholds = new();

/// <summary>
/// The probability that something within a given squared distance is generated.
/// For internal use, _must_ be ordered in descending order of distance.
/// </summary>
[ViewVariables]
public List<WorldGenDistanceThreshold> SquaredDistanceThresholds = new();
}

[DataDefinition]
public sealed partial class WorldGenDistanceThreshold
{
/// <summary>
/// The maximum distance within the threshold.
/// </summary>
[DataField]
public float MaxDistance;

/// <summary>
/// The probability that something within this distance will spawn.
/// </summary>
[DataField]
public float Prob = 1.0f;
}
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// This carves out holes in world gen based on distance from a set of known points.
/// </summary>
public sealed class PointSetDistanceCarverSystem : EntitySystem
{
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly IRobustRandom _random = default!;
// Cache points for lookup

/// <inheritdoc />
public override void Initialize()
{
SubscribeLocalEvent<WorldGenDistanceCarverComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<PointSetDistanceCarverComponent, PrePlaceDebrisFeatureEvent>(OnPrePlaceDebris);
}

private void OnInit(Entity<WorldGenDistanceCarverComponent> 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<WorldGenDistanceCarverComponent, TransformComponent>();
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;
}
}
Loading