Dynamic space world generation and debris. (#15120)
* World generation (squash) * Test fixes. * command * o * Access cleanup. * Documentation touchups. * Use a prototype serializer for BiomeSelectionComponent * Struct enumerator in SimpleFloorPlanPopulatorSystem * Safety margins around PoissonDiskSampler, cookie acquisition methodologies * Struct enumerating PoissonDiskSampler; internal side * Struct enumerating PoissonDiskSampler: Finish it * Update WorldgenConfigSystem.cs awa --------- Co-authored-by: moonheart08 <moonheart08@users.noreply.github.com> Co-authored-by: 20kdc <asdd2808@gmail.com>
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
using Content.Server.Worldgen.Systems.Biomes;
|
||||
using Content.Server.Worldgen.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
||||
|
||||
namespace Content.Server.Worldgen.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for selecting the biome(s) to be used during world generation.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(BiomeSelectionSystem))]
|
||||
public sealed class BiomeSelectionComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of biomes available to this selector.
|
||||
/// </summary>
|
||||
/// <remarks>This is always sorted by priority after ComponentStartup.</remarks>
|
||||
[DataField("biomes", required: true,
|
||||
customTypeSerializer: typeof(PrototypeIdListSerializer<BiomePrototype>))] public List<string> Biomes = new();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
using Content.Server.Worldgen.Prototypes;
|
||||
using Content.Server.Worldgen.Systems.Carvers;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.Worldgen.Components.Carvers;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for carving out empty space in the game world, providing byways through the debris field.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(NoiseRangeCarverSystem))]
|
||||
public sealed class NoiseRangeCarverComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The noise channel to use as a density controller.
|
||||
/// </summary>
|
||||
/// <remarks>This noise channel should be mapped to exactly the range [0, 1] unless you want a lot of warnings in the log.</remarks>
|
||||
[DataField("noiseChannel", customTypeSerializer: typeof(PrototypeIdSerializer<NoiseChannelPrototype>))]
|
||||
public string NoiseChannel { get; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The index of ranges in which to cut debris generation.
|
||||
/// </summary>
|
||||
[DataField("ranges", required: true)]
|
||||
public List<Vector2> Ranges { get; } = default!;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
using Content.Server.Worldgen.Systems.Debris;
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
||||
|
||||
namespace Content.Server.Worldgen.Components.Debris;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for constructing asteroid debris.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(BlobFloorPlanBuilderSystem))]
|
||||
public sealed class BlobFloorPlanBuilderComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The probability that placing a floor tile will add up to three-four neighboring tiles as well.
|
||||
/// </summary>
|
||||
[DataField("blobDrawProb")] public float BlobDrawProb;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum radius for the structure.
|
||||
/// </summary>
|
||||
[DataField("radius", required: true)] public float Radius;
|
||||
|
||||
/// <summary>
|
||||
/// The tiles to be used for the floor plan.
|
||||
/// </summary>
|
||||
[DataField("floorTileset", required: true,
|
||||
customTypeSerializer: typeof(PrototypeIdListSerializer<ContentTileDefinition>))]
|
||||
public List<string> FloorTileset { get; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The number of floor tiles to place when drawing the asteroid layout.
|
||||
/// </summary>
|
||||
[DataField("floorPlacements", required: true)]
|
||||
public int FloorPlacements { get; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
using Content.Server.Worldgen.Prototypes;
|
||||
using Content.Server.Worldgen.Systems.Debris;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.Worldgen.Components.Debris;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for controlling the debris feature placer.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(DebrisFeaturePlacerSystem))]
|
||||
public sealed class DebrisFeaturePlacerControllerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether or not to clip debris that would spawn at a location that has a density of zero.
|
||||
/// </summary>
|
||||
[DataField("densityClip")] public bool DensityClip = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not entities are already spawned.
|
||||
/// </summary>
|
||||
public bool DoSpawns = true;
|
||||
|
||||
[DataField("ownedDebris")] public Dictionary<Vector2, EntityUid?> OwnedDebris = new();
|
||||
|
||||
/// <summary>
|
||||
/// The chance spawning a piece of debris will just be cancelled randomly.
|
||||
/// </summary>
|
||||
[DataField("randomCancelChance")] public float RandomCancellationChance = 0.1f;
|
||||
|
||||
/// <summary>
|
||||
/// Radius in which there should be no objects for debris to spawn.
|
||||
/// </summary>
|
||||
[DataField("safetyZoneRadius")] public float SafetyZoneRadius = 16.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The noise channel to use as a density controller.
|
||||
/// </summary>
|
||||
[DataField("densityNoiseChannel", customTypeSerializer: typeof(PrototypeIdSerializer<NoiseChannelPrototype>))]
|
||||
public string DensityNoiseChannel { get; } = default!;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
using Content.Server.Worldgen.Prototypes;
|
||||
using Content.Server.Worldgen.Systems.Debris;
|
||||
using Content.Server.Worldgen.Tools;
|
||||
using Content.Shared.Storage;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.Worldgen.Components.Debris;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for selecting debris with a probability determined by a noise channel.
|
||||
/// Takes priority over SimpleDebrisSelectorComponent and should likely be used in combination.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(NoiseDrivenDebrisSelectorSystem))]
|
||||
public sealed class NoiseDrivenDebrisSelectorComponent : Component
|
||||
{
|
||||
private EntitySpawnCollectionCache? _cache;
|
||||
|
||||
/// <summary>
|
||||
/// The prototype-facing debris table entries.
|
||||
/// </summary>
|
||||
[DataField("debrisTable", required: true)]
|
||||
private List<EntitySpawnEntry> _entries = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The debris entity spawn collection.
|
||||
/// </summary>
|
||||
public EntitySpawnCollectionCache CachedDebrisTable
|
||||
{
|
||||
get
|
||||
{
|
||||
_cache ??= new EntitySpawnCollectionCache(_entries);
|
||||
return _cache;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The noise channel to use as a density controller.
|
||||
/// </summary>
|
||||
/// <remarks>This noise channel should be mapped to exactly the range [0, 1] unless you want a lot of warnings in the log.</remarks>
|
||||
[DataField("noiseChannel", customTypeSerializer: typeof(PrototypeIdSerializer<NoiseChannelPrototype>))]
|
||||
public string NoiseChannel { get; } = default!;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
using Content.Server.Worldgen.Systems.Debris;
|
||||
|
||||
namespace Content.Server.Worldgen.Components.Debris;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for attaching a piece of debris to it's owning controller.
|
||||
/// Mostly just syncs deletion.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(DebrisFeaturePlacerSystem))]
|
||||
public sealed class OwnedDebrisComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The last location in the controller's internal structure for this debris.
|
||||
/// </summary>
|
||||
[DataField("lastKey")] public Vector2 LastKey;
|
||||
|
||||
/// <summary>
|
||||
/// The DebrisFeaturePlacerController-having entity that owns this.
|
||||
/// </summary>
|
||||
[DataField("owningController")] public EntityUid OwningController;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
using Content.Server.Worldgen.Systems.Debris;
|
||||
using Content.Server.Worldgen.Tools;
|
||||
using Content.Shared.Storage;
|
||||
|
||||
namespace Content.Server.Worldgen.Components.Debris;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for a very simple debris selection for simple biomes. Just uses a spawn table.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(DebrisFeaturePlacerSystem))]
|
||||
public sealed class SimpleDebrisSelectorComponent : Component
|
||||
{
|
||||
private EntitySpawnCollectionCache? _cache;
|
||||
|
||||
/// <summary>
|
||||
/// The prototype-facing debris table entries.
|
||||
/// </summary>
|
||||
[DataField("debrisTable", required: true)]
|
||||
private List<EntitySpawnEntry> _entries = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The debris entity spawn collection.
|
||||
/// </summary>
|
||||
public EntitySpawnCollectionCache CachedDebrisTable
|
||||
{
|
||||
get
|
||||
{
|
||||
_cache ??= new EntitySpawnCollectionCache(_entries);
|
||||
return _cache;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Worldgen.Systems.Debris;
|
||||
using Content.Server.Worldgen.Tools;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Storage;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
|
||||
|
||||
namespace Content.Server.Worldgen.Components.Debris;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for populating a grid with random entities automatically.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(SimpleFloorPlanPopulatorSystem))]
|
||||
public sealed class SimpleFloorPlanPopulatorComponent : Component
|
||||
{
|
||||
private Dictionary<string, EntitySpawnCollectionCache>? _caches;
|
||||
|
||||
/// <summary>
|
||||
/// The prototype facing floor plan populator entries.
|
||||
/// </summary>
|
||||
[DataField("entries", required: true,
|
||||
customTypeSerializer: typeof(PrototypeIdDictionarySerializer<List<EntitySpawnEntry>, ContentTileDefinition>))]
|
||||
private Dictionary<string, List<EntitySpawnEntry>> _entries = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The spawn collections used to place entities on different tile types.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public Dictionary<string, EntitySpawnCollectionCache> Caches
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_caches is null)
|
||||
{
|
||||
_caches = _entries
|
||||
.Select(x =>
|
||||
new KeyValuePair<string, EntitySpawnCollectionCache>(x.Key,
|
||||
new EntitySpawnCollectionCache(x.Value)))
|
||||
.ToDictionary(x => x.Key, x => x.Value);
|
||||
}
|
||||
|
||||
return _caches;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
using Content.Server.Worldgen.Prototypes;
|
||||
using Content.Server.Worldgen.Systems.GC;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.Worldgen.Components.GC;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for whether or not a GCable object is "dirty". Firing GCDirtyEvent on the object is the correct way to
|
||||
/// set this up.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(GCQueueSystem))]
|
||||
public sealed class GCAbleObjectComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Which queue to insert this object into when GCing
|
||||
/// </summary>
|
||||
[DataField("queue", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<GCQueuePrototype>))]
|
||||
public string Queue = default!;
|
||||
}
|
||||
|
||||
17
Content.Server/Worldgen/Components/LoadedChunkComponent.cs
Normal file
17
Content.Server/Worldgen/Components/LoadedChunkComponent.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Content.Server.Worldgen.Systems;
|
||||
|
||||
namespace Content.Server.Worldgen.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for marking a chunk as loaded.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(WorldControllerSystem))]
|
||||
public sealed class LoadedChunkComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The current list of entities loading this chunk.
|
||||
/// </summary>
|
||||
[ViewVariables] public List<EntityUid>? Loaders = null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
using Content.Server.Worldgen.Systems;
|
||||
|
||||
namespace Content.Server.Worldgen.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for sending a signal to the entity it's on to load contents whenever a loader gets close enough.
|
||||
/// Does not support unloading.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(LocalityLoaderSystem))]
|
||||
public sealed class LocalityLoaderComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The maximum distance an entity can be from the loader for it to not load.
|
||||
/// Once a loader is closer than this, the event is fired and this component removed.
|
||||
/// </summary>
|
||||
[DataField("loadingDistance")] public int LoadingDistance = 32;
|
||||
}
|
||||
|
||||
20
Content.Server/Worldgen/Components/NoiseIndexComponent.cs
Normal file
20
Content.Server/Worldgen/Components/NoiseIndexComponent.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Content.Server.Worldgen.Prototypes;
|
||||
using Content.Server.Worldgen.Systems;
|
||||
|
||||
namespace Content.Server.Worldgen.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for containing configured noise generators.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(NoiseIndexSystem))]
|
||||
public sealed class NoiseIndexComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// An index of generators, to avoid having to recreate them every time a noise channel is used.
|
||||
/// Keyed by noise generator prototype ID.
|
||||
/// </summary>
|
||||
[Access(typeof(NoiseIndexSystem), Friend = AccessPermissions.ReadWriteExecute, Other = AccessPermissions.None)]
|
||||
public Dictionary<string, NoiseGenerator> Generators { get; } = new();
|
||||
}
|
||||
|
||||
22
Content.Server/Worldgen/Components/WorldChunkComponent.cs
Normal file
22
Content.Server/Worldgen/Components/WorldChunkComponent.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Content.Server.Worldgen.Systems;
|
||||
|
||||
namespace Content.Server.Worldgen.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for marking an entity as being a world chunk.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(WorldControllerSystem))]
|
||||
public sealed class WorldChunkComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The coordinates of the chunk, in chunk space.
|
||||
/// </summary>
|
||||
[DataField("coordinates")] public Vector2i Coordinates;
|
||||
|
||||
/// <summary>
|
||||
/// The map this chunk belongs to.
|
||||
/// </summary>
|
||||
[DataField("map")] public EntityUid Map;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
using Content.Server.Worldgen.Systems;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.Worldgen.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for controlling overall world loading, containing an index of all chunks in the map.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(WorldControllerSystem))]
|
||||
public sealed class WorldControllerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The prototype to use for chunks on this world map.
|
||||
/// </summary>
|
||||
[DataField("chunkProto", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string ChunkProto = "WorldChunk";
|
||||
|
||||
/// <summary>
|
||||
/// An index of chunks owned by the controller.
|
||||
/// </summary>
|
||||
[DataField("chunks")] public Dictionary<Vector2i, EntityUid> Chunks = new();
|
||||
}
|
||||
|
||||
18
Content.Server/Worldgen/Components/WorldLoaderComponent.cs
Normal file
18
Content.Server/Worldgen/Components/WorldLoaderComponent.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using Content.Server.Worldgen.Systems;
|
||||
|
||||
namespace Content.Server.Worldgen.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for allowing some objects to load the game world.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(WorldControllerSystem))]
|
||||
public sealed class WorldLoaderComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The radius in which the loader loads the world.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("radius")]
|
||||
public int Radius = 128;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user