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