diff --git a/Content.Server/_Citadel/Worldgen/Components/BiomeSelectionComponent.cs b/Content.Server/_Citadel/Worldgen/Components/BiomeSelectionComponent.cs deleted file mode 100644 index f78912c963..0000000000 --- a/Content.Server/_Citadel/Worldgen/Components/BiomeSelectionComponent.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Content.Server._Citadel.Worldgen.Systems.Biomes; - -namespace Content.Server._Citadel.Worldgen.Components; - -/// -/// This is used for selecting the biome(s) to be used during world generation. -/// -[RegisterComponent] -[Access(typeof(BiomeSelectionSystem))] -public sealed class BiomeSelectionComponent : Component -{ - /// - /// The list of biomes available to this selector. - /// - /// This is always sorted by priority after ComponentStartup. - [DataField("biomes", required: true)] public List Biomes = new(); -} - diff --git a/Content.Server/_Citadel/Worldgen/Components/Carvers/NoiseRangeCarverComponent.cs b/Content.Server/_Citadel/Worldgen/Components/Carvers/NoiseRangeCarverComponent.cs deleted file mode 100644 index cc5f19cb52..0000000000 --- a/Content.Server/_Citadel/Worldgen/Components/Carvers/NoiseRangeCarverComponent.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Content.Server._Citadel.Worldgen.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server._Citadel.Worldgen.Components.Carvers; - -/// -/// This is used for carving out empty space in the game world, providing byways through the debris field. -/// -[RegisterComponent] -public sealed class NoiseRangeCarverComponent : Component -{ - /// - /// The noise channel to use as a density controller. - /// - /// This noise channel should be mapped to exactly the range [0, 1] unless you want a lot of warnings in the log. - [DataField("noiseChannel", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string NoiseChannel { get; } = default!; - - /// - /// The index of ranges in which to cut debris generation. - /// - [DataField("ranges", required: true)] - public List Ranges { get; } = default!; -} - diff --git a/Content.Server/_Citadel/Worldgen/Components/Debris/BlobFloorPlanBuilderComponent.cs b/Content.Server/_Citadel/Worldgen/Components/Debris/BlobFloorPlanBuilderComponent.cs deleted file mode 100644 index 3aa50f0232..0000000000 --- a/Content.Server/_Citadel/Worldgen/Components/Debris/BlobFloorPlanBuilderComponent.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Content.Server._Citadel.Worldgen.Systems.Debris; -using Content.Shared.Maps; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; - -namespace Content.Server._Citadel.Worldgen.Components.Debris; - -/// -/// This is used for constructing asteroid debris. -/// -[RegisterComponent] -[Access(typeof(BlobFloorPlanBuilderSystem))] -public sealed class BlobFloorPlanBuilderComponent : Component -{ - /// - /// The probability that placing a floor tile will add up to three-four neighboring tiles as well. - /// - [DataField("blobDrawProb")] public float BlobDrawProb; - - /// - /// The maximum radius for the structure. - /// - [DataField("radius", required: true)] public float Radius; - - /// - /// The tiles to be used for the floor plan. - /// - [DataField("floorTileset", required: true, - customTypeSerializer: typeof(PrototypeIdListSerializer))] - public List FloorTileset { get; } = default!; - - /// - /// The number of floor tiles to place when drawing the asteroid layout. - /// - [DataField("floorPlacements", required: true)] - public int FloorPlacements { get; } -} - diff --git a/Content.Server/_Citadel/Worldgen/Components/Debris/DebrisFeaturePlacerControllerComponent.cs b/Content.Server/_Citadel/Worldgen/Components/Debris/DebrisFeaturePlacerControllerComponent.cs deleted file mode 100644 index df7ed1f587..0000000000 --- a/Content.Server/_Citadel/Worldgen/Components/Debris/DebrisFeaturePlacerControllerComponent.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Content.Server._Citadel.Worldgen.Prototypes; -using Content.Server._Citadel.Worldgen.Systems.Debris; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server._Citadel.Worldgen.Components.Debris; - -/// -/// This is used for controlling the debris feature placer. -/// -[RegisterComponent] -[Access(typeof(DebrisFeaturePlacerSystem))] -public sealed class DebrisFeaturePlacerControllerComponent : Component -{ - /// - /// Whether or not to clip debris that would spawn at a location that has a density of zero. - /// - [DataField("densityClip")] public bool DensityClip = true; - - /// - /// Whether or not entities are already spawned. - /// - public bool DoSpawns = true; - - [DataField("ownedDebris")] public Dictionary OwnedDebris = new(); - - /// - /// The chance spawning a piece of debris will just be cancelled randomly. - /// - [DataField("randomCancelChance")] public float RandomCancellationChance = 0.1f; - - /// - /// Radius in which there should be no objects for debris to spawn. - /// - [DataField("safetyZoneRadius")] public float SafetyZoneRadius = 16.0f; - - /// - /// The noise channel to use as a density controller. - /// - [DataField("densityNoiseChannel", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string DensityNoiseChannel { get; } = default!; -} - diff --git a/Content.Server/_Citadel/Worldgen/Components/Debris/NoiseDrivenDebrisSelectorComponent.cs b/Content.Server/_Citadel/Worldgen/Components/Debris/NoiseDrivenDebrisSelectorComponent.cs deleted file mode 100644 index be939cb25c..0000000000 --- a/Content.Server/_Citadel/Worldgen/Components/Debris/NoiseDrivenDebrisSelectorComponent.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Content.Server._Citadel.Worldgen.Prototypes; -using Content.Server._Citadel.Worldgen.Tools; -using Content.Shared.Storage; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server._Citadel.Worldgen.Components.Debris; - -/// -/// 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. -/// -[RegisterComponent] -public sealed class NoiseDrivenDebrisSelectorComponent : Component -{ - private EntitySpawnCollectionCache? _cache; - - [DataField("debrisTable", required: true)] - private List _entries = default!; - - /// - /// The debris entity spawn collection. - /// - public EntitySpawnCollectionCache CachedDebrisTable - { - get - { - _cache ??= new EntitySpawnCollectionCache(_entries); - return _cache; - } - } - - /// - /// The noise channel to use as a density controller. - /// - /// This noise channel should be mapped to exactly the range [0, 1] unless you want a lot of warnings in the log. - [DataField("noiseChannel", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string NoiseChannel { get; } = default!; -} - diff --git a/Content.Server/_Citadel/Worldgen/Components/Debris/OwnedDebrisComponent.cs b/Content.Server/_Citadel/Worldgen/Components/Debris/OwnedDebrisComponent.cs deleted file mode 100644 index 2dc0bb5afd..0000000000 --- a/Content.Server/_Citadel/Worldgen/Components/Debris/OwnedDebrisComponent.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Content.Server._Citadel.Worldgen.Systems.Debris; - -namespace Content.Server._Citadel.Worldgen.Components.Debris; - -/// -/// This is used for attaching a piece of debris to it's owning controller. -/// Mostly just syncs deletion. -/// -[RegisterComponent] -[Access(typeof(DebrisFeaturePlacerSystem))] -public sealed class OwnedDebrisComponent : Component -{ - /// - /// The last location in the controller's internal structure for this debris. - /// - [DataField("lastKey")] public Vector2 LastKey; - - /// - /// The DebrisFeaturePlacerController-having entity that owns this. - /// - [DataField("owningController")] public EntityUid OwningController; -} - diff --git a/Content.Server/_Citadel/Worldgen/Components/Debris/SimpleDebrisSelectorComponent.cs b/Content.Server/_Citadel/Worldgen/Components/Debris/SimpleDebrisSelectorComponent.cs deleted file mode 100644 index 36a3398689..0000000000 --- a/Content.Server/_Citadel/Worldgen/Components/Debris/SimpleDebrisSelectorComponent.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Content.Server._Citadel.Worldgen.Systems.Debris; -using Content.Server._Citadel.Worldgen.Tools; -using Content.Shared.Storage; - -namespace Content.Server._Citadel.Worldgen.Components.Debris; - -/// -/// This is used for a very simple debris selection for simple biomes. Just uses a spawn table. -/// -[RegisterComponent] -[Access(typeof(DebrisFeaturePlacerSystem))] -public sealed class SimpleDebrisSelectorComponent : Component -{ - private EntitySpawnCollectionCache? _cache; - - [DataField("debrisTable", required: true)] - private List _entries = default!; - - /// - /// The debris entity spawn collection. - /// - public EntitySpawnCollectionCache CachedDebrisTable - { - get - { - _cache ??= new EntitySpawnCollectionCache(_entries); - return _cache; - } - } -} - diff --git a/Content.Server/_Citadel/Worldgen/Components/Debris/SimpleFloorPlanPopulatorComponent.cs b/Content.Server/_Citadel/Worldgen/Components/Debris/SimpleFloorPlanPopulatorComponent.cs deleted file mode 100644 index 99369a5511..0000000000 --- a/Content.Server/_Citadel/Worldgen/Components/Debris/SimpleFloorPlanPopulatorComponent.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Linq; -using Content.Server._Citadel.Worldgen.Systems.Debris; -using Content.Server._Citadel.Worldgen.Tools; -using Content.Shared.Maps; -using Content.Shared.Storage; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; - -namespace Content.Server._Citadel.Worldgen.Components.Debris; - -/// -/// This is used for populating a grid with random entities automatically. -/// -[RegisterComponent] -[Access(typeof(SimpleFloorPlanPopulatorSystem))] -public sealed class SimpleFloorPlanPopulatorComponent : Component -{ - private Dictionary? _caches; - - [DataField("entries", required: true, - customTypeSerializer: typeof(PrototypeIdDictionarySerializer, ContentTileDefinition>))] - private Dictionary> _entries = default!; - - /// - /// The spawn collections used to place entities on different tile types. - /// - [ViewVariables] - public Dictionary Caches - { - get - { - if (_caches is null) - { - _caches = _entries - .Select(x => - new KeyValuePair(x.Key, - new EntitySpawnCollectionCache(x.Value))) - .ToDictionary(x => x.Key, x => x.Value); - } - - return _caches; - } - } -} - diff --git a/Content.Server/_Citadel/Worldgen/Components/GC/GCAbleObjectComponent.cs b/Content.Server/_Citadel/Worldgen/Components/GC/GCAbleObjectComponent.cs deleted file mode 100644 index 5f9c284b90..0000000000 --- a/Content.Server/_Citadel/Worldgen/Components/GC/GCAbleObjectComponent.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Content.Server._Citadel.Worldgen.Prototypes; -using Content.Server._Citadel.Worldgen.Systems.GC; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server._Citadel.Worldgen.Components.GC; - -/// -/// 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. -/// -[RegisterComponent] -[Access(typeof(GCQueueSystem))] -public sealed class GCAbleObjectComponent : Component -{ - /// - /// Which queue to insert this object into when GCing - /// - [DataField("queue", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] - public string Queue = default!; -} - diff --git a/Content.Server/_Citadel/Worldgen/Components/LoadedChunkComponent.cs b/Content.Server/_Citadel/Worldgen/Components/LoadedChunkComponent.cs deleted file mode 100644 index daafe26b3b..0000000000 --- a/Content.Server/_Citadel/Worldgen/Components/LoadedChunkComponent.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Content.Server._Citadel.Worldgen.Systems; - -namespace Content.Server._Citadel.Worldgen.Components; - -/// -/// This is used for marking a chunk as loaded. -/// -[RegisterComponent] -[Access(typeof(WorldControllerSystem))] -public sealed class LoadedChunkComponent : Component -{ - /// - /// The current list of entities loading this chunk. - /// - [ViewVariables] public List? Loaders = null; -} - diff --git a/Content.Server/_Citadel/Worldgen/Components/LocalityLoaderComponent.cs b/Content.Server/_Citadel/Worldgen/Components/LocalityLoaderComponent.cs deleted file mode 100644 index 2566ac0c17..0000000000 --- a/Content.Server/_Citadel/Worldgen/Components/LocalityLoaderComponent.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Content.Server._Citadel.Worldgen.Systems; - -namespace Content.Server._Citadel.Worldgen.Components; - -/// -/// 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. -/// -[RegisterComponent] -[Access(typeof(LocalityLoaderSystem))] -public sealed class LocalityLoaderComponent : Component -{ - /// - /// 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. - /// - [DataField("loadingDistance")] public int LoadingDistance = 32; -} - diff --git a/Content.Server/_Citadel/Worldgen/Components/NoiseIndexComponent.cs b/Content.Server/_Citadel/Worldgen/Components/NoiseIndexComponent.cs deleted file mode 100644 index f9248ac279..0000000000 --- a/Content.Server/_Citadel/Worldgen/Components/NoiseIndexComponent.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Content.Server._Citadel.Worldgen.Prototypes; -using Content.Server._Citadel.Worldgen.Systems; - -namespace Content.Server._Citadel.Worldgen.Components; - -/// -/// This is used for containing configured noise generators. -/// -[RegisterComponent] -[Access(typeof(NoiseIndexSystem))] -public sealed class NoiseIndexComponent : Component -{ - /// - /// An index of generators, to avoid having to recreate them every time a noise channel is used. - /// Keyed by noise generator prototype ID. - /// - [Access(typeof(NoiseIndexSystem), Friend = AccessPermissions.ReadWriteExecute, Other = AccessPermissions.None)] - public Dictionary Generators { get; } = new(); -} - diff --git a/Content.Server/_Citadel/Worldgen/Components/WorldChunkComponent.cs b/Content.Server/_Citadel/Worldgen/Components/WorldChunkComponent.cs deleted file mode 100644 index 70415e0cd2..0000000000 --- a/Content.Server/_Citadel/Worldgen/Components/WorldChunkComponent.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Content.Server._Citadel.Worldgen.Systems; - -namespace Content.Server._Citadel.Worldgen.Components; - -/// -/// This is used for marking an entity as being a world chunk. -/// -[RegisterComponent] -[Access(typeof(WorldControllerSystem))] -public sealed class WorldChunkComponent : Component -{ - /// - /// The coordinates of the chunk, in chunk space. - /// - [DataField("coordinates")] public Vector2i Coordinates; - - /// - /// The map this chunk belongs to. - /// - [DataField("map")] public EntityUid Map; -} - diff --git a/Content.Server/_Citadel/Worldgen/Components/WorldControllerComponent.cs b/Content.Server/_Citadel/Worldgen/Components/WorldControllerComponent.cs deleted file mode 100644 index 57f3210ee0..0000000000 --- a/Content.Server/_Citadel/Worldgen/Components/WorldControllerComponent.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Content.Server._Citadel.Worldgen.Systems; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server._Citadel.Worldgen.Components; - -/// -/// This is used for controlling overall world loading, containing an index of all chunks in the map. -/// -[RegisterComponent] -[Access(typeof(WorldControllerSystem))] -public sealed class WorldControllerComponent : Component -{ - [DataField("chunkProto", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string ChunkProto = "CitadelChunk"; - - /// - /// An index of chunks owned by the controller. - /// - [DataField("chunks")] public Dictionary Chunks = new(); -} - diff --git a/Content.Server/_Citadel/Worldgen/Components/WorldLoaderComponent.cs b/Content.Server/_Citadel/Worldgen/Components/WorldLoaderComponent.cs deleted file mode 100644 index 532d6a3655..0000000000 --- a/Content.Server/_Citadel/Worldgen/Components/WorldLoaderComponent.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Content.Server._Citadel.Worldgen.Systems; - -namespace Content.Server._Citadel.Worldgen.Components; - -/// -/// This is used for allowing some objects to load the game world. -/// -[RegisterComponent] -[Access(typeof(WorldControllerSystem))] -public sealed class WorldLoaderComponent : Component -{ - /// - /// The radius in which the loader loads the world. - /// - [ViewVariables(VVAccess.ReadWrite)] [DataField("radius")] - public int Radius = 128; -} - diff --git a/Content.Server/_Citadel/Worldgen/GridPointsNearEnumerator.cs b/Content.Server/_Citadel/Worldgen/GridPointsNearEnumerator.cs deleted file mode 100644 index 9f1f260873..0000000000 --- a/Content.Server/_Citadel/Worldgen/GridPointsNearEnumerator.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; - -namespace Content.Server._Citadel.Worldgen; - -/// -/// A struct enumerator of points on a grid within the given radius. -/// -public struct GridPointsNearEnumerator -{ - private readonly int _radius; - private readonly Vector2i _center; - private int _x; - private int _y; - - public GridPointsNearEnumerator(Vector2i center, int radius) - { - _radius = radius; - _center = center; - _x = -_radius; - _y = -_radius; - } - - /// - /// Gets the next point in the enumeration. - /// - /// The computed point, if any - /// Success - [Pure] - public bool MoveNext([NotNullWhen(true)] out Vector2i? chunk) - { - while (!(_x * _x + _y * _y <= _radius * _radius)) - { - if (_y > _radius) - { - chunk = null; - return false; - } - - if (_x > _radius) - { - _x = -_radius; - _y++; - } - else - { - _x++; - } - } - - chunk = _center + new Vector2i(_x, _y); - _x++; - return true; - } -} - diff --git a/Content.Server/_Citadel/Worldgen/Prototypes/BiomePrototype.cs b/Content.Server/_Citadel/Worldgen/Prototypes/BiomePrototype.cs deleted file mode 100644 index 05ec7c894e..0000000000 --- a/Content.Server/_Citadel/Worldgen/Prototypes/BiomePrototype.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.Manager; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array; - -namespace Content.Server._Citadel.Worldgen.Prototypes; - -/// -/// This is a prototype for biome selection, allowing the component list of a chunk to be amended based on the output -/// of noise channels at that location. -/// -[Prototype("citadelBiome")] -public sealed class BiomePrototype : IPrototype, IInheritingPrototype -{ - /// - [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))] - public string[]? Parents { get; } - - /// - [NeverPushInheritance] - [AbstractDataField] - public bool Abstract { get; } - - /// - [IdDataField] - public string ID { get; } = default!; - - /// - /// The valid ranges of noise values under which this biome can be picked. - /// - [DataField("noiseRanges", required: true)] - public Dictionary> NoiseRanges = default!; - - /// - /// Higher priority biomes get picked before lower priority ones. - /// - [DataField("priority", required: true)] - public int Priority { get; } - - /// - /// The components that get added to the target map. - /// - [DataField("chunkComponents")] - [AlwaysPushInheritance] - public EntityPrototype.ComponentRegistry ChunkComponents { get; } = new(); - - //TODO: Get someone to make this a method on componentregistry that does it Correctly. - /// - /// Applies the worldgen config to the given target (presumably a map.) - /// - public void Apply(EntityUid target, ISerializationManager serialization, IEntityManager entityManager) - { - // Add all components required by the prototype. Engine update for this whenst. - foreach (var data in ChunkComponents.Values) - { - var comp = (Component) serialization.CreateCopy(data.Component, notNullableOverride: true); - comp.Owner = target; - entityManager.AddComponent(target, comp); - } - } -} - diff --git a/Content.Server/_Citadel/Worldgen/Prototypes/GCQueuePrototype.cs b/Content.Server/_Citadel/Worldgen/Prototypes/GCQueuePrototype.cs deleted file mode 100644 index 89f9b830c2..0000000000 --- a/Content.Server/_Citadel/Worldgen/Prototypes/GCQueuePrototype.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Robust.Shared.Prototypes; - -namespace Content.Server._Citadel.Worldgen.Prototypes; - -/// -/// This is a prototype for a GC queue. -/// -[Prototype("gcQueue")] -public sealed class GCQueuePrototype : IPrototype -{ - /// - [IdDataField] - public string ID { get; } = default!; - - /// - /// How deep the GC queue is at most. If this value is ever exceeded entities get processed automatically regardless of - /// tick-time cap. - /// - [DataField("depth", required: true)] - public int Depth { get; } - - /// - /// The maximum amount of time that can be spent processing this queue. - /// - [DataField("maximumTickTime")] - public TimeSpan MaximumTickTime { get; } = TimeSpan.FromMilliseconds(1); - - /// - /// The minimum depth before entities in the queue actually get processed for deletion. - /// - [DataField("minDepthToProcess", required: true)] - public int MinDepthToProcess { get; } - - /// - /// Whether or not the GC should fire an event on the entity to see if it's eligible to skip the queue. - /// Useful for making it so only objects a player has actually interacted with get put in the collection queue. - /// - [DataField("trySkipQueue")] - public bool TrySkipQueue { get; } -} - diff --git a/Content.Server/_Citadel/Worldgen/Prototypes/NoiseChannelPrototype.cs b/Content.Server/_Citadel/Worldgen/Prototypes/NoiseChannelPrototype.cs deleted file mode 100644 index f309508eb4..0000000000 --- a/Content.Server/_Citadel/Worldgen/Prototypes/NoiseChannelPrototype.cs +++ /dev/null @@ -1,166 +0,0 @@ -using Robust.Shared.Noise; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array; - -namespace Content.Server._Citadel.Worldgen.Prototypes; - -/// -/// This is a config for noise channels, used by worldgen. -/// -[Virtual] -public class NoiseChannelConfig -{ - /// - /// The noise type used by the noise generator. - /// - [DataField("noiseType")] - public FastNoise.NoiseType NoiseType { get; } = FastNoise.NoiseType.Cellular; - - /// - /// The fractal type used by the noise generator. - /// - [DataField("fractalType")] - public FastNoise.FractalType FractalType { get; } = FastNoise.FractalType.Billow; - - /// - /// Multiplied by pi in code when used. - /// - [DataField("fractalLacunarityByPi")] - public float FractalLacunarityByPi { get; } = 2.0f / 3.0f; - - /// - /// Ranges of values that get clamped down to the "clipped" value. - /// - [DataField("clippingRanges")] - public List ClippingRanges { get; } = new(); - - /// - /// The value clipped chunks are set to. - /// - [DataField("clippedValue")] - public float ClippedValue { get; } - - /// - /// A value the output is multiplied by. - /// - [DataField("outputMultiplier")] - public float OutputMultiplier { get; } = 1.0f; - - /// - /// A value the input is multiplied by. - /// - [DataField("inputMultiplier")] - public float InputMultiplier { get; } = 1.0f; - - /// - /// Remaps the output of the noise function from the range (-1, 1) to (0, 1). This is done before all other output - /// transformations. - /// - [DataField("remapTo0Through1")] - public bool RemapTo0Through1 { get; } - - /// - /// For when the transformation you need is too complex to describe in YAML. - /// - [DataField("noisePostProcess")] - public NoisePostProcess? NoisePostProcess { get; } - - /// - /// For when you need a complex transformation of the input coordinates. - /// - [DataField("noiseCoordinateProcess")] - public NoiseCoordinateProcess? NoiseCoordinateProcess { get; } - - /// - /// The "center" of the range of values. Or the minimum if mapped 0 through 1. - /// - [DataField("minimum")] - public float Minimum { get; } -} - -[Prototype("noiseChannel")] -public sealed class NoiseChannelPrototype : NoiseChannelConfig, IPrototype, IInheritingPrototype -{ - /// - [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))] - public string[]? Parents { get; } - - /// - [NeverPushInheritance] - [AbstractDataField] - public bool Abstract { get; } - - /// - [IdDataField] - public string ID { get; } = default!; -} - -/// -/// A wrapper around FastNoise's noise generation, using noise channel configs. -/// -public struct NoiseGenerator -{ - private readonly NoiseChannelConfig _config; - private readonly FastNoise _noise; - - public NoiseGenerator(NoiseChannelConfig config, int seed) - { - _config = config; - _noise = new FastNoise(); - _noise.SetSeed(seed); - _noise.SetNoiseType(_config.NoiseType); - _noise.SetFractalType(_config.FractalType); - _noise.SetFractalLacunarity(_config.FractalLacunarityByPi * MathF.PI); - } - - /// - /// Evaluates the noise generator at the provided coordinates. - /// - /// Coordinates to use as input - /// Computed noise value - public float Evaluate(Vector2 coords) - { - var finCoords = coords * _config.InputMultiplier; - - if (_config.NoiseCoordinateProcess is not null) - finCoords = _config.NoiseCoordinateProcess.Process(finCoords); - - var value = _noise.GetNoise(finCoords.X, finCoords.Y); - - if (_config.RemapTo0Through1) - value = (value + 1.0f) / 2.0f; - - foreach (var range in _config.ClippingRanges) - { - if (range.X < value && value < range.Y) - { - value = _config.ClippedValue; - break; - } - } - - if (_config.NoisePostProcess is not null) - value = _config.NoisePostProcess.Process(value); - value *= _config.OutputMultiplier; - return value + _config.Minimum; - } -} - -/// -/// A processing class that adjusts the input coordinate space to a noise channel. -/// -[ImplicitDataDefinitionForInheritors] -public abstract class NoiseCoordinateProcess -{ - public abstract Vector2 Process(Vector2 inp); -} - -/// -/// A processing class that adjusts the final result of the noise channel. -/// -[ImplicitDataDefinitionForInheritors] -public abstract class NoisePostProcess -{ - public abstract float Process(float inp); -} - diff --git a/Content.Server/_Citadel/Worldgen/Prototypes/WorldgenConfigPrototype.cs b/Content.Server/_Citadel/Worldgen/Prototypes/WorldgenConfigPrototype.cs deleted file mode 100644 index e22aecae50..0000000000 --- a/Content.Server/_Citadel/Worldgen/Prototypes/WorldgenConfigPrototype.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.Manager; - -namespace Content.Server._Citadel.Worldgen.Prototypes; - -/// -/// This is a prototype for controlling overall world generation. -/// The components included are applied to the map that world generation is configured on. -/// -[Prototype("worldgenConfig")] -public sealed class WorldgenConfigPrototype : IPrototype -{ - /// - [IdDataField] - public string ID { get; } = default!; - - /// - /// The components that get added to the target map. - /// - [DataField("components", required: true)] - public EntityPrototype.ComponentRegistry Components { get; } = default!; - - //TODO: Get someone to make this a method on componentregistry that does it Correctly. - /// - /// Applies the worldgen config to the given target (presumably a map.) - /// - public void Apply(EntityUid target, ISerializationManager serialization, IEntityManager entityManager) - { - // Add all components required by the prototype. Engine update for this whenst. - foreach (var data in Components.Values) - { - var comp = (Component) serialization.CreateCopy(data.Component, notNullableOverride: true); - comp.Owner = target; - entityManager.AddComponent(target, comp); - } - } -} - diff --git a/Content.Server/_Citadel/Worldgen/Systems/BaseWorldSystem.cs b/Content.Server/_Citadel/Worldgen/Systems/BaseWorldSystem.cs deleted file mode 100644 index 5338543204..0000000000 --- a/Content.Server/_Citadel/Worldgen/Systems/BaseWorldSystem.cs +++ /dev/null @@ -1,58 +0,0 @@ -using Content.Server._Citadel.Worldgen.Components; -using JetBrains.Annotations; - -namespace Content.Server._Citadel.Worldgen.Systems; - -/// -/// This provides some additional functions for world generation systems. -/// Exists primarily for convenience and to avoid code duplication. -/// -[PublicAPI] -public abstract class BaseWorldSystem : EntitySystem -{ - [Dependency] private readonly WorldControllerSystem _worldController = default!; - - /// - /// Gets a chunk's coordinates in chunk space as an integer value. - /// - /// - /// - /// Chunk space coordinates - [Pure] - public Vector2i GetChunkCoords(EntityUid ent, TransformComponent? xform = null) - { - if (!Resolve(ent, ref xform)) - throw new Exception("Failed to resolve transform, somehow."); - - return WorldGen.WorldToChunkCoords(xform.WorldPosition).Floored(); - } - - /// - /// Gets a chunk's coordinates in chunk space as a floating point value. - /// - /// - /// - /// Chunk space coordinates - [Pure] - public Vector2 GetFloatingChunkCoords(EntityUid ent, TransformComponent? xform = null) - { - if (!Resolve(ent, ref xform)) - throw new Exception("Failed to resolve transform, somehow."); - - return WorldGen.WorldToChunkCoords(xform.WorldPosition); - } - - /// - /// Attempts to get a chunk, creating it if it doesn't exist. - /// - /// Chunk coordinates to get the chunk entity for. - /// Map the chunk is in. - /// The controller this chunk belongs to. - /// A chunk, if available. - [Pure] - public EntityUid? GetOrCreateChunk(Vector2i chunk, EntityUid map, WorldControllerComponent? controller = null) - { - return _worldController.GetOrCreateChunk(chunk, map, controller); - } -} - diff --git a/Content.Server/_Citadel/Worldgen/Systems/Biomes/BiomeSelectionSystem.cs b/Content.Server/_Citadel/Worldgen/Systems/Biomes/BiomeSelectionSystem.cs deleted file mode 100644 index ec9055f1a0..0000000000 --- a/Content.Server/_Citadel/Worldgen/Systems/Biomes/BiomeSelectionSystem.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.Linq; -using Content.Server._Citadel.Worldgen.Components; -using Content.Server._Citadel.Worldgen.Prototypes; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.Manager; - -namespace Content.Server._Citadel.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; - foreach (var biomeId in component.Biomes) - { - var biome = _proto.Index(biomeId); - if (!CheckBiomeValidity(args.Chunk, biome, coords)) - continue; - - biome.Apply(args.Chunk, _ser, EntityManager); - return; - } - - Logger.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. - } - - 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; - } -} - diff --git a/Content.Server/_Citadel/Worldgen/Systems/Carvers/NoiseRangeCarverSystem.cs b/Content.Server/_Citadel/Worldgen/Systems/Carvers/NoiseRangeCarverSystem.cs deleted file mode 100644 index 39af927e8e..0000000000 --- a/Content.Server/_Citadel/Worldgen/Systems/Carvers/NoiseRangeCarverSystem.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Content.Server._Citadel.Worldgen.Components.Carvers; -using Content.Server._Citadel.Worldgen.Systems.Debris; - -namespace Content.Server._Citadel.Worldgen.Systems.Carvers; - -/// -/// This handles carving out holes in world generation according to a noise channel. -/// -public sealed class NoiseRangeCarverSystem : EntitySystem -{ - [Dependency] private readonly NoiseIndexSystem _index = default!; - - /// - public override void Initialize() - { - SubscribeLocalEvent(OnPrePlaceDebris); - } - - private void OnPrePlaceDebris(EntityUid uid, NoiseRangeCarverComponent component, - ref PrePlaceDebrisFeatureEvent args) - { - var coords = WorldGen.WorldToChunkCoords(args.Coords.ToMapPos(EntityManager)); - var val = _index.Evaluate(uid, component.NoiseChannel, coords); - - foreach (var (low, high) in component.Ranges) - { - if (low > val || high < val) - continue; - - args.Handled = true; - return; - } - } -} - diff --git a/Content.Server/_Citadel/Worldgen/Systems/Debris/BlobFloorPlanBuilderSystem.cs b/Content.Server/_Citadel/Worldgen/Systems/Debris/BlobFloorPlanBuilderSystem.cs deleted file mode 100644 index 4e2c4191dc..0000000000 --- a/Content.Server/_Citadel/Worldgen/Systems/Debris/BlobFloorPlanBuilderSystem.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System.Linq; -using Content.Server._Citadel.Worldgen.Components.Debris; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Random; - -namespace Content.Server._Citadel.Worldgen.Systems.Debris; - -/// -/// This handles building the floor plans for "blobby" debris. -/// -public sealed class BlobFloorPlanBuilderSystem : BaseWorldSystem -{ - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefinition = default!; - - /// - public override void Initialize() - { - SubscribeLocalEvent(OnBlobFloorPlanBuilderStartup); - } - - private void OnBlobFloorPlanBuilderStartup(EntityUid uid, BlobFloorPlanBuilderComponent component, - ComponentStartup args) - { - PlaceFloorplanTiles(component, Comp(uid)); - } - - private void PlaceFloorplanTiles(BlobFloorPlanBuilderComponent comp, MapGridComponent grid) - { - // NO MORE THAN TWO ALLOCATIONS THANK YOU VERY MUCH. - var spawnPoints = new HashSet(comp.FloorPlacements * 6); - var taken = new Dictionary(comp.FloorPlacements * 5); - - void PlaceTile(Vector2i point) - { - // Assume we already know that the spawn point is safe. - spawnPoints.Remove(point); - var north = point.Offset(Direction.North); - var south = point.Offset(Direction.South); - var east = point.Offset(Direction.East); - var west = point.Offset(Direction.West); - var radsq = Math.Pow(comp.Radius, - 2); // I'd put this outside but i'm not 100% certain caching it between calls is a gain. - - // The math done is essentially a fancy way of comparing the distance from 0,0 to the radius, - // and skipping the sqrt normally needed for dist. - if (!taken.ContainsKey(north) && Math.Pow(north.X, 2) + Math.Pow(north.Y, 2) <= radsq) - spawnPoints.Add(north); - if (!taken.ContainsKey(south) && Math.Pow(south.X, 2) + Math.Pow(south.Y, 2) <= radsq) - spawnPoints.Add(south); - if (!taken.ContainsKey(east) && Math.Pow(east.X, 2) + Math.Pow(east.Y, 2) <= radsq) - spawnPoints.Add(east); - if (!taken.ContainsKey(west) && Math.Pow(west.X, 2) + Math.Pow(west.Y, 2) <= radsq) - spawnPoints.Add(west); - - taken.Add(point, new Tile(_tileDefinition[_random.Pick(comp.FloorTileset)].TileId)); - } - - PlaceTile(Vector2i.Zero); - - for (var i = 0; i < comp.FloorPlacements; i++) - { - var point = _random.Pick(spawnPoints); - PlaceTile(point); - - if (comp.BlobDrawProb > 0.0f) - { - if (!taken.ContainsKey(point.Offset(Direction.North)) && _random.Prob(comp.BlobDrawProb)) - PlaceTile(point.Offset(Direction.North)); - if (!taken.ContainsKey(point.Offset(Direction.South)) && _random.Prob(comp.BlobDrawProb)) - PlaceTile(point.Offset(Direction.South)); - if (!taken.ContainsKey(point.Offset(Direction.East)) && _random.Prob(comp.BlobDrawProb)) - PlaceTile(point.Offset(Direction.East)); - if (!taken.ContainsKey(point.Offset(Direction.West)) && _random.Prob(comp.BlobDrawProb)) - PlaceTile(point.Offset(Direction.West)); - } - } - - grid.SetTiles(taken.Select(x => (x.Key, x.Value)).ToList()); - } -} - diff --git a/Content.Server/_Citadel/Worldgen/Systems/Debris/DebrisFeaturePlacerSystem.cs b/Content.Server/_Citadel/Worldgen/Systems/Debris/DebrisFeaturePlacerSystem.cs deleted file mode 100644 index d5804cab8d..0000000000 --- a/Content.Server/_Citadel/Worldgen/Systems/Debris/DebrisFeaturePlacerSystem.cs +++ /dev/null @@ -1,261 +0,0 @@ -using System.Linq; -using Content.Server._Citadel.Worldgen.Components; -using Content.Server._Citadel.Worldgen.Components.Debris; -using Content.Server._Citadel.Worldgen.Systems.GC; -using Content.Server._Citadel.Worldgen.Tools; -using JetBrains.Annotations; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Random; - -namespace Content.Server._Citadel.Worldgen.Systems.Debris; - -/// -/// This handles placing debris within the world evenly with rng, primarily for structures like asteroid fields. -/// -public sealed class DebrisFeaturePlacerSystem : BaseWorldSystem -{ - [Dependency] private readonly GCQueueSystem _gc = default!; - [Dependency] private readonly ILogManager _logManager = default!; - [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly NoiseIndexSystem _noiseIndex = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly PoissonDiskSampler _sampler = default!; - - private ISawmill _sawmill = default!; - - /// - public override void Initialize() - { - _sawmill = _logManager.GetSawmill("world.debris.feature_placer"); - SubscribeLocalEvent(OnChunkLoaded); - SubscribeLocalEvent(OnChunkUnloaded); - SubscribeLocalEvent(OnDebrisShutdown); - SubscribeLocalEvent(OnDebrisMove); - SubscribeLocalEvent(OnTryCancelGC); - SubscribeLocalEvent( - OnTryGetPlacableDebrisEvent); - } - - /// - /// Handles GC cancellation in case the chunk is still loaded. - /// - private void OnTryCancelGC(EntityUid uid, OwnedDebrisComponent component, ref TryCancelGC args) - { - args.Cancelled |= HasComp(component.OwningController); - } - - /// - /// Handles debris moving, and making sure it stays parented to a chunk for loading purposes. - /// - private void OnDebrisMove(EntityUid uid, OwnedDebrisComponent component, ref MoveEvent args) - { - if (!HasComp(component.OwningController)) - return; // Redundant logic, prolly needs it's own handler for your custom system. - - var placer = Comp(component.OwningController); - var xform = Transform(uid); - var ownerXform = Transform(component.OwningController); - if (xform.MapUid is null || ownerXform.MapUid is null) - return; // not our problem - - if (xform.MapUid != ownerXform.MapUid) - { - _sawmill.Error($"Somehow debris {uid} left it's expected map! Unparenting it to avoid issues."); - RemCompDeferred(uid); - placer.OwnedDebris.Remove(component.LastKey); - return; - } - - placer.OwnedDebris.Remove(component.LastKey); - var newChunk = GetOrCreateChunk(GetChunkCoords(uid), xform.MapUid!.Value); - if (newChunk is null || !TryComp(newChunk, out var newPlacer)) - { - // Whelp. - RemCompDeferred(uid); - return; - } - - newPlacer.OwnedDebris[xform.WorldPosition] = uid; // Change our owner. - component.OwningController = newChunk.Value; - } - - /// - /// Handles debris shutdown/detach. - /// - private void OnDebrisShutdown(EntityUid uid, OwnedDebrisComponent component, ComponentShutdown args) - { - if (!TryComp(component.OwningController, out var placer)) - return; - - placer.OwnedDebris[component.LastKey] = null; - if (Terminating(uid)) - placer.OwnedDebris.Remove(component.LastKey); - } - - /// - /// Queues all debris owned by the placer for garbage collection. - /// - private void OnChunkUnloaded(EntityUid uid, DebrisFeaturePlacerControllerComponent component, - ref WorldChunkUnloadedEvent args) - { - foreach (var (_, debris) in component.OwnedDebris) - { - if (debris is not null) - _gc.TryGCEntity(debris.Value); // gonb. - } - - component.DoSpawns = true; - } - - /// - /// Handles providing a debris type to place for SimpleDebrisSelectorComponent. - /// This randomly picks a debris type from the EntitySpawnCollectionCache. - /// - private void OnTryGetPlacableDebrisEvent(EntityUid uid, SimpleDebrisSelectorComponent component, - ref TryGetPlaceableDebrisFeatureEvent args) - { - if (args.DebrisProto is not null) - return; - - var l = new List(1); - component.CachedDebrisTable.GetSpawns(_random, ref l); - - switch (l.Count) - { - case 0: - return; - case > 1: - _sawmill.Warning($"Got more than one possible debris type from {uid}. List: {string.Join(", ", l)}"); - break; - } - - args.DebrisProto = l[0]; - } - - /// - /// Handles loading in debris. This does the following: - /// - Checks if the debris is currently supposed to do spawns, if it isn't, aborts immediately. - /// - Evaluates the density value to be used for placement, if it's zero, aborts. - /// - Generates the points to generate debris at, if and only if they've not been selected already by a prior load. - /// - Does the following in a loop over all generated points: - /// - Raises an event to check if something else wants to intercept debris placement, if the event is handled, - /// continues to the next point without generating anything. - /// - Raises an event to get the debris type that should be used for generation. - /// - Spawns the given debris at the point, adding it to the placer's index. - /// - private void OnChunkLoaded(EntityUid uid, DebrisFeaturePlacerControllerComponent component, - ref WorldChunkLoadedEvent args) - { - if (component.DoSpawns == false) - return; - - component.DoSpawns = false; // Don't repeat yourself if this crashes. - - var chunk = Comp(args.Chunk); - var densityChannel = component.DensityNoiseChannel; - var density = _noiseIndex.Evaluate(uid, densityChannel, chunk.Coordinates + new Vector2(0.5f, 0.5f)); - if (density == 0) - return; - - List? points = null; - - // If we've been loaded before, reuse the same coordinates. - if (component.OwnedDebris.Count != 0) - { - //TODO: Remove LINQ. - points = component.OwnedDebris - .Where(x => !Deleted(x.Value)) - .Select(static x => x.Key) - .ToList(); - } - - points ??= GeneratePointsInChunk(args.Chunk, density, chunk.Coordinates, chunk.Map); - - var safetyBounds = Box2.UnitCentered.Enlarged(component.SafetyZoneRadius); - var failures = 0; // Avoid severe log spam. - foreach (var point in points) - { - var pointDensity = _noiseIndex.Evaluate(uid, densityChannel, WorldGen.WorldToChunkCoords(point)); - if (pointDensity == 0 && component.DensityClip || _random.Prob(component.RandomCancellationChance)) - continue; - - var coords = new EntityCoordinates(chunk.Map, point); - - if (_mapManager - .FindGridsIntersecting(Comp(chunk.Map).WorldMap, safetyBounds.Translated(point)).Any()) - continue; // Oops, gonna collide. - - var preEv = new PrePlaceDebrisFeatureEvent(coords, args.Chunk); - RaiseLocalEvent(uid, ref preEv); - if (uid != args.Chunk) - RaiseLocalEvent(args.Chunk, ref preEv); - - if (preEv.Handled) - continue; - - var debrisFeatureEv = new TryGetPlaceableDebrisFeatureEvent(coords, args.Chunk); - RaiseLocalEvent(uid, ref debrisFeatureEv); - - if (debrisFeatureEv.DebrisProto == null) - { - // Try on the chunk...? - if (uid != args.Chunk) - RaiseLocalEvent(args.Chunk, ref debrisFeatureEv); - - if (debrisFeatureEv.DebrisProto == null) - { - // Nope. - failures++; - continue; - } - } - - var ent = Spawn(debrisFeatureEv.DebrisProto, coords); - component.OwnedDebris.Add(point, ent); - - var owned = EnsureComp(ent); - owned.OwningController = uid; - owned.LastKey = point; - } - - if (failures > 0) - _sawmill.Error($"Failed to place {failures} debris at chunk {args.Chunk}"); - } - - /// - /// Generates the points to put into a chunk using a poisson disk sampler. - /// - private List GeneratePointsInChunk(EntityUid chunk, float density, Vector2 coords, EntityUid map) - { - var offs = (int) ((WorldGen.ChunkSize - WorldGen.ChunkSize / 8.0f) / 2.0f); - var topLeft = (-offs, -offs); - var lowerRight = (offs, offs); - var debrisPoints = _sampler.SampleRectangle(topLeft, lowerRight, density); - - var realCenter = WorldGen.ChunkToWorldCoordsCentered(coords.Floored()); - - for (var i = 0; i < debrisPoints.Count; i++) - { - debrisPoints[i] = realCenter + debrisPoints[i]; - } - - return debrisPoints; - } -} - -/// -/// Fired directed on the debris feature placer controller and the chunk, ahead of placing a debris piece. -/// -[ByRefEvent] -[PublicAPI] -public record struct PrePlaceDebrisFeatureEvent(EntityCoordinates Coords, EntityUid Chunk, bool Handled = false); - -/// -/// Fired directed on the debris feature placer controller and the chunk, to select which debris piece to place. -/// -[ByRefEvent] -[PublicAPI] -public record struct TryGetPlaceableDebrisFeatureEvent(EntityCoordinates Coords, EntityUid Chunk, - string? DebrisProto = null); - diff --git a/Content.Server/_Citadel/Worldgen/Systems/Debris/NoiseDrivenDebrisSelectorSystem.cs b/Content.Server/_Citadel/Worldgen/Systems/Debris/NoiseDrivenDebrisSelectorSystem.cs deleted file mode 100644 index 73d0ad88f7..0000000000 --- a/Content.Server/_Citadel/Worldgen/Systems/Debris/NoiseDrivenDebrisSelectorSystem.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Content.Server._Citadel.Worldgen.Components.Debris; -using Robust.Shared.Random; - -namespace Content.Server._Citadel.Worldgen.Systems.Debris; - -/// -/// This handles selecting debris with probability decided by a noise channel. -/// -public sealed class NoiseDrivenDebrisSelectorSystem : BaseWorldSystem -{ - [Dependency] private readonly NoiseIndexSystem _index = default!; - [Dependency] private readonly ILogManager _logManager = default!; - [Dependency] private readonly IRobustRandom _random = default!; - - private ISawmill _sawmill = default!; - - /// - public override void Initialize() - { - _sawmill = _logManager.GetSawmill("world.debris.noise_debris_selector"); - // Event is forcibly ordered to always be handled after the simple selector. - SubscribeLocalEvent(OnSelectDebrisKind, - after: new[] {typeof(DebrisFeaturePlacerSystem)}); - } - - private void OnSelectDebrisKind(EntityUid uid, NoiseDrivenDebrisSelectorComponent component, - ref TryGetPlaceableDebrisFeatureEvent args) - { - var coords = WorldGen.WorldToChunkCoords(args.Coords.ToMapPos(EntityManager)); - var prob = _index.Evaluate(uid, component.NoiseChannel, coords); - - if (prob is < 0 or > 1) - { - _sawmill.Error( - $"Sampled a probability of {prob}, which is outside the [0, 1] range, at {coords} aka {args.Coords}."); - return; - } - - if (!_random.Prob(prob)) - return; - - var l = new List(1); - component.CachedDebrisTable.GetSpawns(_random, ref l); - - switch (l.Count) - { - case 0: - return; - case > 1: - _sawmill.Warning($"Got more than one possible debris type from {uid}. List: {string.Join(", ", l)}"); - break; - } - - args.DebrisProto = l[0]; - } -} - diff --git a/Content.Server/_Citadel/Worldgen/Systems/Debris/SimpleFloorPlanPopulatorSystem.cs b/Content.Server/_Citadel/Worldgen/Systems/Debris/SimpleFloorPlanPopulatorSystem.cs deleted file mode 100644 index c9e335dddd..0000000000 --- a/Content.Server/_Citadel/Worldgen/Systems/Debris/SimpleFloorPlanPopulatorSystem.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Content.Server._Citadel.Worldgen.Components.Debris; -using Content.Shared.Maps; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Random; - -namespace Content.Server._Citadel.Worldgen.Systems.Debris; - -/// -/// This handles populating simple structures, simply using a loot table for each tile. -/// -public sealed class SimpleFloorPlanPopulatorSystem : BaseWorldSystem -{ - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefinition = default!; - - /// - public override void Initialize() - { - SubscribeLocalEvent(OnFloorPlanBuilt); - } - - private void OnFloorPlanBuilt(EntityUid uid, SimpleFloorPlanPopulatorComponent component, - LocalStructureLoadedEvent args) - { - var placeables = new List(4); - var grid = Comp(uid); - foreach (var tile in grid.GetAllTiles()) - { - var coords = grid.GridTileToLocal(tile.GridIndices); - var selector = tile.Tile.GetContentTileDefinition(_tileDefinition).ID; - if (!component.Caches.TryGetValue(selector, out var cache)) - continue; - - placeables.Clear(); - cache.GetSpawns(_random, ref placeables); - - foreach (var proto in placeables) - { - if (proto is null) - continue; - - Spawn(proto, coords); - } - } - } -} - diff --git a/Content.Server/_Citadel/Worldgen/Systems/GC/GCQueueSystem.cs b/Content.Server/_Citadel/Worldgen/Systems/GC/GCQueueSystem.cs deleted file mode 100644 index 0f8d7c547a..0000000000 --- a/Content.Server/_Citadel/Worldgen/Systems/GC/GCQueueSystem.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System.Linq; -using Content.Server._Citadel.Worldgen.Components.GC; -using Content.Server._Citadel.Worldgen.Prototypes; -using JetBrains.Annotations; -using Robust.Shared.Configuration; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; -using Robust.Shared.Timing; - -namespace Content.Server._Citadel.Worldgen.Systems.GC; - -/// -/// This handles delayed garbage collection of entities, to avoid overloading the tick in particularly expensive cases. -/// -public sealed class GCQueueSystem : EntitySystem -{ - [Dependency] private readonly IConfigurationManager _cfg = default!; - [Dependency] private readonly IPrototypeManager _proto = default!; - [Dependency] private readonly IRobustRandom _random = default!; - - [ViewVariables] private TimeSpan _maximumProcessTime = TimeSpan.Zero; - - [ViewVariables] private readonly Dictionary> _queues = new(); - - /// - public override void Initialize() - { - _cfg.OnValueChanged(WorldgenCVars.GCMaximumTimeMs, s => _maximumProcessTime = TimeSpan.FromMilliseconds(s), - true); - } - - /// - public override void Update(float frameTime) - { - var overallWatch = new Stopwatch(); - var queueWatch = new Stopwatch(); - var queues = _queues.ToList(); - _random.Shuffle(queues); // Avert resource starvation by always processing in random order. - overallWatch.Start(); - foreach (var (pId, queue) in queues) - { - if (overallWatch.Elapsed > _maximumProcessTime) - return; - - var proto = _proto.Index(pId); - if (queue.Count < proto.MinDepthToProcess) - continue; - - queueWatch.Restart(); - while (queueWatch.Elapsed < proto.MaximumTickTime && queue.Count >= proto.MinDepthToProcess && - overallWatch.Elapsed < _maximumProcessTime) - { - var e = queue.Dequeue(); - if (!Deleted(e)) - { - var ev = new TryCancelGC(); - RaiseLocalEvent(e, ref ev); - - if (!ev.Cancelled) - Del(e); - } - } - } - } - - /// - /// Attempts to GC an entity. This functions as QueueDel if it can't. - /// - /// Entity to GC. - public void TryGCEntity(EntityUid e) - { - if (!TryComp(e, out var comp)) - { - QueueDel(e); // not our problem :) - return; - } - - if (!_queues.TryGetValue(comp.Queue, out var queue)) - { - queue = new Queue(); - _queues[comp.Queue] = queue; - } - - var proto = _proto.Index(comp.Queue); - if (queue.Count > proto.Depth) - { - QueueDel(e); // whelp, too full. - return; - } - - if (proto.TrySkipQueue) - { - var ev = new TryGCImmediately(); - RaiseLocalEvent(e, ref ev); - if (!ev.Cancelled) - { - QueueDel(e); - return; - } - } - - queue.Enqueue(e); - } -} - -/// -/// Fired by GCQueueSystem to check if it can simply immediately GC an entity, for example if it was never fully -/// loaded. -/// -/// Whether or not the immediate deletion attempt was cancelled. -[ByRefEvent] -[PublicAPI] -public record struct TryGCImmediately(bool Cancelled = false); - -/// -/// Fired by GCQueueSystem to check if the collection of the given entity should be cancelled, for example it's chunk -/// being loaded again. -/// -/// Whether or not the deletion attempt was cancelled. -[ByRefEvent] -[PublicAPI] -public record struct TryCancelGC(bool Cancelled = false); - diff --git a/Content.Server/_Citadel/Worldgen/Systems/LocalityLoaderSystem.cs b/Content.Server/_Citadel/Worldgen/Systems/LocalityLoaderSystem.cs deleted file mode 100644 index 27db6e71e8..0000000000 --- a/Content.Server/_Citadel/Worldgen/Systems/LocalityLoaderSystem.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Content.Server._Citadel.Worldgen.Components; - -namespace Content.Server._Citadel.Worldgen.Systems; - -/// -/// This handles loading in objects based on distance from player, using some metadata on chunks. -/// -public sealed class LocalityLoaderSystem : BaseWorldSystem -{ - /// - public override void Update(float frameTime) - { - var e = EntityQueryEnumerator(); - var loadedQuery = GetEntityQuery(); - var xformQuery = GetEntityQuery(); - var controllerQuery = GetEntityQuery(); - - while (e.MoveNext(out var loadable, out var xform)) - { - if (!controllerQuery.TryGetComponent(xform.MapUid, out var controller)) - return; - - var coords = GetChunkCoords(xform.Owner, xform); - var done = false; - for (var i = -1; i < 2 && !done; i++) - { - for (var j = -1; j < 2 && !done; j++) - { - var chunk = GetOrCreateChunk(coords + (i, j), xform.MapUid!.Value, controller); - if (!loadedQuery.TryGetComponent(chunk, out var loaded) || loaded.Loaders is null) - continue; - - foreach (var loader in loaded.Loaders) - { - if (!xformQuery.TryGetComponent(loader, out var loaderXform)) - continue; - - if ((loaderXform.WorldPosition - xform.WorldPosition).Length > loadable.LoadingDistance) - continue; - - RaiseLocalEvent(loadable.Owner, new LocalStructureLoadedEvent()); - RemCompDeferred(loadable.Owner); - done = true; - break; - } - } - } - } - } -} - -public record struct LocalStructureLoadedEvent; - diff --git a/Content.Server/_Citadel/Worldgen/Systems/NoiseIndexSystem.cs b/Content.Server/_Citadel/Worldgen/Systems/NoiseIndexSystem.cs deleted file mode 100644 index 6eeaba33a5..0000000000 --- a/Content.Server/_Citadel/Worldgen/Systems/NoiseIndexSystem.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Content.Server._Citadel.Worldgen.Components; -using Content.Server._Citadel.Worldgen.Prototypes; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; - -namespace Content.Server._Citadel.Worldgen.Systems; - -/// -/// This handles the noise index. -/// -public sealed class NoiseIndexSystem : EntitySystem -{ - [Dependency] private readonly IPrototypeManager _prototype = default!; - [Dependency] private readonly IRobustRandom _random = default!; - - /// - /// Gets a particular noise channel from the index on the given entity. - /// - /// The holder of the index - /// The channel prototype ID - /// An initialized noise generator - public NoiseGenerator Get(EntityUid holder, string protoId) - { - var idx = EnsureComp(holder); - if (idx.Generators.TryGetValue(protoId, out var generator)) - return generator; - var proto = _prototype.Index(protoId); - var gen = new NoiseGenerator(proto, _random.Next()); - idx.Generators[protoId] = gen; - return gen; - } - - /// - /// Attempts to evaluate the given noise channel using the generator on the given entity. - /// - /// The holder of the index - /// The channel prototype ID - /// The coordinates to evaluate at - /// The result of evaluation - public float Evaluate(EntityUid holder, string protoId, Vector2 coords) - { - var gen = Get(holder, protoId); - return gen.Evaluate(coords); - } -} - diff --git a/Content.Server/_Citadel/Worldgen/Systems/WorldControllerSystem.cs b/Content.Server/_Citadel/Worldgen/Systems/WorldControllerSystem.cs deleted file mode 100644 index d2f8bb2eff..0000000000 --- a/Content.Server/_Citadel/Worldgen/Systems/WorldControllerSystem.cs +++ /dev/null @@ -1,262 +0,0 @@ -using System.Linq; -using Content.Server._Citadel.Worldgen.Components; -using Content.Server.Ghost.Components; -using Content.Server.Mind.Components; -using JetBrains.Annotations; -using Robust.Shared.Map; -using Robust.Shared.Timing; - -namespace Content.Server._Citadel.Worldgen.Systems; - -/// -/// This handles putting together chunk entities and notifying them about important changes. -/// -public sealed class WorldControllerSystem : EntitySystem -{ - private const int PlayerLoadRadius = 2; - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly ILogManager _logManager = default!; - - private ISawmill _sawmill = default!; - - /// - public override void Initialize() - { - _sawmill = _logManager.GetSawmill("world"); - SubscribeLocalEvent(OnChunkLoadedCore); - SubscribeLocalEvent(OnChunkUnloadedCore); - SubscribeLocalEvent(OnChunkShutdown); - } - - /// - /// Handles deleting chunks properly. - /// - private void OnChunkShutdown(EntityUid uid, WorldChunkComponent component, ComponentShutdown args) - { - if (!TryComp(component.Map, out var controller)) - return; - - if (HasComp(uid)) - { - var ev = new WorldChunkUnloadedEvent(uid, component.Coordinates); - RaiseLocalEvent(component.Map, ref ev); - RaiseLocalEvent(uid, ref ev); - } - - controller.Chunks.Remove(component.Coordinates); - } - - /// - /// Handles the inner logic of loading a chunk, i.e. events. - /// - private void OnChunkLoadedCore(EntityUid uid, LoadedChunkComponent component, ComponentStartup args) - { - if (!TryComp(uid, out var chunk)) - return; - - var ev = new WorldChunkLoadedEvent(uid, chunk.Coordinates); - RaiseLocalEvent(chunk.Map, ref ev); - RaiseLocalEvent(uid, ref ev); - //_sawmill.Debug($"Loaded chunk {ToPrettyString(uid)} at {chunk.Coordinates}"); - } - - /// - /// Handles the inner logic of unloading a chunk, i.e. events. - /// - private void OnChunkUnloadedCore(EntityUid uid, LoadedChunkComponent component, ComponentShutdown args) - { - if (!TryComp(uid, out var chunk)) - return; - - if (Terminating(uid)) - return; // SAFETY: This is in case a loaded chunk gets deleted, to avoid double unload. - - var ev = new WorldChunkUnloadedEvent(uid, chunk.Coordinates); - RaiseLocalEvent(chunk.Map, ref ev); - RaiseLocalEvent(uid, ref ev); - //_sawmill.Debug($"Unloaded chunk {ToPrettyString(uid)} at {coords}"); - } - - /// - public override void Update(float frameTime) - { - //there was a to-do here about every frame alloc but it turns out it's a nothing burger here. - var chunksToLoad = new Dictionary>>(); - - foreach (var controller in EntityQuery()) - { - chunksToLoad[controller.Owner] = new Dictionary>(); - } - - var loaderEnum = EntityQueryEnumerator(); - - while (loaderEnum.MoveNext(out var worldLoader, out var xform)) - { - var mapOrNull = xform.MapUid; - if (mapOrNull is null) - continue; - var map = mapOrNull.Value; - if (!chunksToLoad.ContainsKey(map)) - continue; - - var wc = xform.WorldPosition; - var coords = WorldGen.WorldToChunkCoords(wc); - var chunks = new GridPointsNearEnumerator(coords.Floored(), - (int) Math.Ceiling(worldLoader.Radius / (float) WorldGen.ChunkSize) + 1); - - var set = chunksToLoad[map]; - - while (chunks.MoveNext(out var chunk)) - { - if (!set.TryGetValue(chunk.Value, out _)) - set[chunk.Value] = new List(4); - set[chunk.Value].Add(worldLoader.Owner); - } - } - - var mindEnum = EntityQueryEnumerator(); - var ghostQuery = GetEntityQuery(); - - // Mindful entities get special privilege as they're always a player and we don't want the illusion being broken around them. - while (mindEnum.MoveNext(out var mind, out var xform)) - { - if (!mind.HasMind) - continue; - if (ghostQuery.HasComponent(mind.Owner)) - continue; - var mapOrNull = xform.MapUid; - if (mapOrNull is null) - continue; - var map = mapOrNull.Value; - if (!chunksToLoad.ContainsKey(map)) - continue; - - var wc = xform.WorldPosition; - var coords = WorldGen.WorldToChunkCoords(wc); - var chunks = new GridPointsNearEnumerator(coords.Floored(), PlayerLoadRadius); - - var set = chunksToLoad[map]; - - while (chunks.MoveNext(out var chunk)) - { - if (!set.TryGetValue(chunk.Value, out _)) - set[chunk.Value] = new List(4); - set[chunk.Value].Add(mind.Owner); - } - } - - var loadedEnum = EntityQueryEnumerator(); - var chunksUnloaded = 0; - - // Make sure these chunks get unloaded at the end of the tick. - while (loadedEnum.MoveNext(out var _, out var chunk)) - { - var coords = chunk.Coordinates; - - if (!chunksToLoad[chunk.Map].ContainsKey(coords)) - { - RemCompDeferred(chunk.Owner); - chunksUnloaded++; - } - } - - if (chunksUnloaded > 0) - _sawmill.Debug($"Queued {chunksUnloaded} chunks for unload."); - - if (chunksToLoad.All(x => x.Value.Count == 0)) - return; - - var startTime = _gameTiming.RealTime; - var count = 0; - var loadedQuery = GetEntityQuery(); - var controllerQuery = GetEntityQuery(); - foreach (var (map, chunks) in chunksToLoad) - { - var controller = controllerQuery.GetComponent(map); - foreach (var (chunk, loaders) in chunks) - { - var ent = GetOrCreateChunk(chunk, map, controller); // Ensure everything loads. - LoadedChunkComponent? c = null; - if (ent is not null && !loadedQuery.TryGetComponent(ent.Value, out c)) - { - c = AddComp(ent.Value); - count += 1; - } - - if (c is not null) - c.Loaders = loaders; - } - } - - if (count > 0) - { - var timeSpan = _gameTiming.RealTime - startTime; - _sawmill.Debug($"Loaded {count} chunks in {timeSpan.TotalMilliseconds:N2}ms."); - } - } - - /// - /// Attempts to get a chunk, creating it if it doesn't exist. - /// - /// Chunk coordinates to get the chunk entity for. - /// Map the chunk is in. - /// The controller this chunk belongs to. - /// A chunk, if available. - [Pure] - public EntityUid? GetOrCreateChunk(Vector2i chunk, EntityUid map, WorldControllerComponent? controller = null) - { - if (!Resolve(map, ref controller)) - throw new Exception($"Tried to use {ToPrettyString(map)} as a world map, without actually being one."); - - if (controller.Chunks.TryGetValue(chunk, out var ent)) - return ent; - return CreateChunkEntity(chunk, map, controller); - } - - /// - /// Constructs a new chunk entity, attaching it to the map. - /// - /// The coordinates the new chunk should be initialized for. - /// - /// - /// - private EntityUid CreateChunkEntity(Vector2i chunkCoords, EntityUid map, WorldControllerComponent controller) - { - var chunk = Spawn(controller.ChunkProto, MapCoordinates.Nullspace); - StartupChunkEntity(chunk, chunkCoords, map, controller); - var md = MetaData(chunk); - md.EntityName = $"Chunk {chunkCoords.X}/{chunkCoords.Y}"; - return chunk; - } - - private void StartupChunkEntity(EntityUid chunk, Vector2i coords, EntityUid map, - WorldControllerComponent controller) - { - if (!TryComp(chunk, out var chunkComponent)) - { - _sawmill.Error($"Chunk {ToPrettyString(chunk)} is missing WorldChunkComponent."); - return; - } - - ref var chunks = ref controller.Chunks; - - chunks[coords] = chunk; // Add this entity to chunk index. - chunkComponent.Coordinates = coords; - chunkComponent.Map = map; - var ev = new WorldChunkAddedEvent(chunk, coords); - RaiseLocalEvent(map, ref ev); - } -} - -[ByRefEvent] -[PublicAPI] -public readonly record struct WorldChunkAddedEvent(EntityUid Chunk, Vector2i Coords); - -[ByRefEvent] -[PublicAPI] -public readonly record struct WorldChunkLoadedEvent(EntityUid Chunk, Vector2i Coords); - -[ByRefEvent] -[PublicAPI] -public readonly record struct WorldChunkUnloadedEvent(EntityUid Chunk, Vector2i Coords); - diff --git a/Content.Server/_Citadel/Worldgen/Systems/WorldgenConfigSystem.cs b/Content.Server/_Citadel/Worldgen/Systems/WorldgenConfigSystem.cs deleted file mode 100644 index e6d9dcd836..0000000000 --- a/Content.Server/_Citadel/Worldgen/Systems/WorldgenConfigSystem.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Content.Server._Citadel.Worldgen.Components; -using Content.Server._Citadel.Worldgen.Prototypes; -using Content.Server.GameTicking; -using Content.Server.GameTicking.Events; -using Robust.Shared.Configuration; -using Robust.Shared.Map; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.Manager; -using Robust.Shared.Utility; - -namespace Content.Server._Citadel.Worldgen.Systems; - -/// -/// This handles configuring world generation during round start. -/// -public sealed class WorldgenConfigSystem : EntitySystem -{ - [Dependency] private readonly IConfigurationManager _cfg = default!; - [Dependency] private readonly GameTicker _gameTicker = default!; - [Dependency] private readonly IMapManager _map = default!; - [Dependency] private readonly IPrototypeManager _proto = default!; - [Dependency] private readonly ISerializationManager _ser = default!; - - private bool _enabled; - private string _worldgenConfig = default!; - - /// - public override void Initialize() - { - SubscribeLocalEvent(OnLoadingMaps); - _cfg.OnValueChanged(WorldgenCVars.WorldgenEnabled, b => _enabled = b, true); - _cfg.OnValueChanged(WorldgenCVars.WorldgenConfig, s => _worldgenConfig = s, true); - } - - /// - /// Applies the world config to the default map if enabled. - /// - private void OnLoadingMaps(RoundStartingEvent ev) - { - if (_enabled == false) - return; - - var target = _map.GetMapEntityId(_gameTicker.DefaultMap); - Logger.Debug($"Trying to configure {_gameTicker.DefaultMap}, aka {ToPrettyString(target)} aka {target}"); - var cfg = _proto.Index(_worldgenConfig); - - cfg.Apply(target, _ser, EntityManager); // Apply the config to the map. - - DebugTools.Assert(HasComp(target)); - } -} - diff --git a/Content.Server/_Citadel/Worldgen/Tools/EntitySpawnCollectionCache.cs b/Content.Server/_Citadel/Worldgen/Tools/EntitySpawnCollectionCache.cs deleted file mode 100644 index 18e6b95ffd..0000000000 --- a/Content.Server/_Citadel/Worldgen/Tools/EntitySpawnCollectionCache.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System.Linq; -using Content.Shared.Storage; -using Robust.Shared.Random; - -namespace Content.Server._Citadel.Worldgen.Tools; - -/// -/// A faster version of EntitySpawnCollection that requires caching to work. -/// -public sealed class EntitySpawnCollectionCache -{ - [ViewVariables] private readonly Dictionary _orGroups = new(); - - public EntitySpawnCollectionCache(IEnumerable entries) - { - // collect groups together, create singular items that pass probability - foreach (var entry in entries) - { - if (!_orGroups.TryGetValue(entry.GroupId ?? string.Empty, out var orGroup)) - { - orGroup = new OrGroup(); - _orGroups.Add(entry.GroupId ?? string.Empty, orGroup); - } - - orGroup.Entries.Add(entry); - orGroup.CumulativeProbability += entry.SpawnProbability; - } - } - - /// - /// Using a collection of entity spawn entries, picks a random list of entity prototypes to spawn from that collection. - /// - /// - /// This does not spawn the entities. The caller is responsible for doing so, since it may want to do something - /// special to those entities (offset them, insert them into storage, etc) - /// - /// Resolve param. - /// List that spawned entities are inserted into. - /// A list of entity prototypes that should be spawned. - /// This is primarily useful if you're calling it many times over, as it lets you reuse the list repeatedly. - public void GetSpawns(IRobustRandom random, ref List spawned) - { - // handle orgroup spawns - foreach (var spawnValue in _orGroups.Values) - { - //HACK: This doesn't seem to work without this if there's only a single orgroup entry. Not sure how to fix the original math properly, but it works in every other case. - if (spawnValue.Entries.Count == 1) - { - var entry = spawnValue.Entries.First(); - var amount = entry.Amount; - - if (entry.MaxAmount > amount) - amount = random.Next(amount, entry.MaxAmount); - - for (var index = 0; index < amount; index++) - { - spawned.Add(entry.PrototypeId); - } - - continue; - } - - // For each group use the added cumulative probability to roll a double in that range - var diceRoll = random.NextDouble() * spawnValue.CumulativeProbability; - // Add the entry's spawn probability to this value, if equals or lower, spawn item, otherwise continue to next item. - var cumulative = 0.0; - foreach (var entry in spawnValue.Entries) - { - cumulative += entry.SpawnProbability; - if (diceRoll > cumulative) - continue; - // Dice roll succeeded, add item and break loop - - var amount = entry.Amount; - - if (entry.MaxAmount > amount) - amount = random.Next(amount, entry.MaxAmount); - - for (var index = 0; index < amount; index++) - { - spawned.Add(entry.PrototypeId); - } - - break; - } - } - } - - private sealed class OrGroup - { - [ViewVariables] public List Entries { get; } = new(); - - [ViewVariables] public float CumulativeProbability { get; set; } - } -} - diff --git a/Content.Server/_Citadel/Worldgen/Tools/PoissonDiskSampler.cs b/Content.Server/_Citadel/Worldgen/Tools/PoissonDiskSampler.cs deleted file mode 100644 index b3d8cef015..0000000000 --- a/Content.Server/_Citadel/Worldgen/Tools/PoissonDiskSampler.cs +++ /dev/null @@ -1,194 +0,0 @@ -using Robust.Shared.Random; - -namespace Content.Server._Citadel.Worldgen.Tools; - -/// -/// An implementation of Poisson Disk Sampling, for evenly spreading points across a given area. -/// -public sealed class PoissonDiskSampler -{ - public const int DefaultPointsPerIteration = 30; - [Dependency] private readonly IRobustRandom _random = default!; - - /// - /// Samples for points within the given circle. - /// - /// Center of the sample - /// Radius of the sample - /// Minimum distance between points - /// The number of points placed per iteration of the algorithm - /// A list of points - public List SampleCircle(Vector2 center, float radius, float minimumDistance, - int pointsPerIteration = DefaultPointsPerIteration) - { - return Sample(center - new Vector2(radius, radius), center + new Vector2(radius, radius), radius, - minimumDistance, pointsPerIteration); - } - - /// - /// Samples for points within the given rectangle. - /// - /// The top left of the rectangle - /// The bottom right of the rectangle - /// Minimum distance between points - /// The number of points placed per iteration of the algorithm - /// A list of points - public List SampleRectangle(Vector2 topLeft, Vector2 lowerRight, float minimumDistance, - int pointsPerIteration = DefaultPointsPerIteration) - { - return Sample(topLeft, lowerRight, null, minimumDistance, pointsPerIteration); - } - - /// - /// Samples for points within the given rectangle, with an optional rejection distance. - /// - /// The top left of the rectangle - /// The bottom right of the rectangle - /// The distance at which points will be discarded, if any - /// Minimum distance between points - /// The number of points placed per iteration of the algorithm - /// A list of points - public List Sample(Vector2 topLeft, Vector2 lowerRight, float? rejectionDistance, - float minimumDistance, int pointsPerIteration) - { - var settings = new SampleSettings - { - TopLeft = topLeft, LowerRight = lowerRight, - Dimensions = lowerRight - topLeft, - Center = (topLeft + lowerRight) / 2, - CellSize = minimumDistance / (float) Math.Sqrt(2), - MinimumDistance = minimumDistance, - RejectionSqDistance = rejectionDistance * rejectionDistance - }; - - settings.GridWidth = (int) (settings.Dimensions.X / settings.CellSize) + 1; - settings.GridHeight = (int) (settings.Dimensions.Y / settings.CellSize) + 1; - - var state = new State - { - Grid = new Vector2?[settings.GridWidth, settings.GridHeight], - ActivePoints = new List(), - Points = new List() - }; - - AddFirstPoint(ref settings, ref state); - - while (state.ActivePoints.Count != 0) - { - var listIndex = _random.Next(state.ActivePoints.Count); - - var point = state.ActivePoints[listIndex]; - var found = false; - - for (var k = 0; k < pointsPerIteration; k++) - { - found |= AddNextPoint(point, ref settings, ref state); - } - - if (!found) - state.ActivePoints.RemoveAt(listIndex); - } - - return state.Points; - } - - private void AddFirstPoint(ref SampleSettings settings, ref State state) - { - var added = false; - while (!added) - { - var d = _random.NextDouble(); - var xr = settings.TopLeft.X + settings.Dimensions.X * d; - - d = _random.NextDouble(); - var yr = settings.TopLeft.Y + settings.Dimensions.Y * d; - - var p = new Vector2((float) xr, (float) yr); - if (settings.RejectionSqDistance != null && - (settings.Center - p).LengthSquared > settings.RejectionSqDistance) - continue; - added = true; - - var index = Denormalize(p, settings.TopLeft, settings.CellSize); - - state.Grid[(int) index.X, (int) index.Y] = p; - - state.ActivePoints.Add(p); - state.Points.Add(p); - } - } - - private bool AddNextPoint(Vector2 point, ref SampleSettings settings, ref State state) - { - var found = false; - var q = GenerateRandomAround(point, settings.MinimumDistance); - - if (q.X >= settings.TopLeft.X && q.X < settings.LowerRight.X && - q.Y > settings.TopLeft.Y && q.Y < settings.LowerRight.Y && - (settings.RejectionSqDistance == null || - (settings.Center - q).LengthSquared <= settings.RejectionSqDistance)) - { - var qIndex = Denormalize(q, settings.TopLeft, settings.CellSize); - var tooClose = false; - - for (var i = (int) Math.Max(0, qIndex.X - 2); - i < Math.Min(settings.GridWidth, qIndex.X + 3) && !tooClose; - i++) - for (var j = (int) Math.Max(0, qIndex.Y - 2); - j < Math.Min(settings.GridHeight, qIndex.Y + 3) && !tooClose; - j++) - { - if (state.Grid[i, j].HasValue && (state.Grid[i, j]!.Value - q).Length < settings.MinimumDistance) - tooClose = true; - } - - if (!tooClose) - { - found = true; - state.ActivePoints.Add(q); - state.Points.Add(q); - state.Grid[(int) qIndex.X, (int) qIndex.Y] = q; - } - } - - return found; - } - - private Vector2 GenerateRandomAround(Vector2 center, float minimumDistance) - { - var d = _random.NextDouble(); - var radius = minimumDistance + minimumDistance * d; - - d = _random.NextDouble(); - var angle = Math.PI * 2 * d; - - var newX = radius * Math.Sin(angle); - var newY = radius * Math.Cos(angle); - - return new Vector2((float) (center.X + newX), (float) (center.Y + newY)); - } - - private static Vector2 Denormalize(Vector2 point, Vector2 origin, double cellSize) - { - return new Vector2((int) ((point.X - origin.X) / cellSize), (int) ((point.Y - origin.Y) / cellSize)); - } - - private struct State - { - public Vector2?[,] Grid; - public List ActivePoints, Points; - } - - private struct SampleSettings - { - public Vector2 TopLeft, LowerRight, Center; - public Vector2 Dimensions; - public float? RejectionSqDistance; - public float MinimumDistance; - public float CellSize; - public int GridWidth, GridHeight; - } -} - - - diff --git a/Content.Server/_Citadel/Worldgen/WorldGen.cs b/Content.Server/_Citadel/Worldgen/WorldGen.cs deleted file mode 100644 index 7d260d2ff5..0000000000 --- a/Content.Server/_Citadel/Worldgen/WorldGen.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System.Diagnostics.Contracts; - -namespace Content.Server._Citadel.Worldgen; - -/// -/// Contains a few world-generation related constants and static functions. -/// -public static class WorldGen -{ - /// - /// The size of each chunk (isn't that self-explanatory.) - /// Be careful about how small you make this. - /// - public const int ChunkSize = 128; - - /// - /// Converts world coordinates to chunk coordinates. - /// - /// World coordinates - /// Chunk coordinates - [Pure] - public static Vector2i WorldToChunkCoords(Vector2i inp) - { - return ((Vector2) inp * (1.0f / ChunkSize, 1.0f / ChunkSize)).Floored(); - } - - /// - /// Converts world coordinates to chunk coordinates. - /// - /// World coordinates - /// Chunk coordinates - [Pure] - public static Vector2 WorldToChunkCoords(Vector2 inp) - { - return inp * (1.0f / ChunkSize, 1.0f / ChunkSize); - } - - /// - /// Converts chunk coordinates to world coordinates. - /// - /// Chunk coordinates - /// World coordinates - [Pure] - public static Vector2 ChunkToWorldCoords(Vector2i inp) - { - return inp * ChunkSize; - } - - /// - /// Converts chunk coordinates to world coordinates. - /// - /// Chunk coordinates - /// World coordinates - [Pure] - public static Vector2 ChunkToWorldCoords(Vector2 inp) - { - return inp * ChunkSize; - } - - /// - /// Converts chunk coordinates to world coordinates, getting the center of the chunk. - /// - /// Chunk coordinates - /// World coordinates - [Pure] - public static Vector2 ChunkToWorldCoordsCentered(Vector2i inp) - { - return inp * ChunkSize + Vector2i.One * (ChunkSize / 2); - } -} - diff --git a/Content.Server/_Citadel/Worldgen/WorldgenCVars.cs b/Content.Server/_Citadel/Worldgen/WorldgenCVars.cs deleted file mode 100644 index a4f754dbfa..0000000000 --- a/Content.Server/_Citadel/Worldgen/WorldgenCVars.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Robust.Shared.Configuration; - -namespace Content.Server._Citadel.Worldgen; - -[CVarDefs] -public sealed class WorldgenCVars -{ - /// - /// Whether or not world generation is enabled. - /// - public static readonly CVarDef WorldgenEnabled = - CVarDef.Create("citadel.worldgen.enabled", true, CVar.SERVERONLY); - - /// - /// The worldgen config to use. - /// - public static readonly CVarDef WorldgenConfig = - CVarDef.Create("citadel.worldgen.worldgen_config", "Default", CVar.SERVERONLY); - - /// - /// The maximum amount of time the GC can process, in ms. - /// - public static readonly CVarDef GCMaximumTimeMs = - CVarDef.Create("citadel.gc.maximum_time_ms", 5, CVar.SERVERONLY); -} - diff --git a/Resources/Prototypes/_Citadel/Entities/World/Debris/asteroids.yml b/Resources/Prototypes/_Citadel/Entities/World/Debris/asteroids.yml deleted file mode 100644 index e99d58d9c1..0000000000 --- a/Resources/Prototypes/_Citadel/Entities/World/Debris/asteroids.yml +++ /dev/null @@ -1,63 +0,0 @@ -- type: entity - id: CitadelBaseAsteroidDebris - parent: CitadelBaseDebris - name: Asteroid Debris - abstract: true - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorTileset: - - FloorAsteroidCoarseSand0 - blobDrawProb: 0.5 - radius: 6 - floorPlacements: 16 - - type: SimpleFloorPlanPopulator - entries: - FloorAsteroidCoarseSand0: - - id: AsteroidRockMining - - type: GCAbleObject - queue: SpaceDebris - - type: IFF - flags: HideLabel - color: "#d67e27" - -- type: entity - id: CitadelAsteroidDebrisSmall - parent: CitadelBaseAsteroidDebris - name: Asteroid Debris Small - noSpawn: true - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorPlacements: 8 - -- type: entity - id: CitadelAsteroidDebrisMedium - parent: CitadelBaseAsteroidDebris - name: Asteroid Debris Medium - noSpawn: true - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorPlacements: 16 - -- type: entity - id: CitadelAsteroidDebrisLarge - parent: CitadelBaseAsteroidDebris - name: Asteroid Debris Large - noSpawn: true - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorPlacements: 24 - -- type: entity - id: CitadelAsteroidDebrisLarger - parent: CitadelBaseAsteroidDebris - name: Asteroid Debris Larger - noSpawn: true - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - radius: 12 - floorPlacements: 36 diff --git a/Resources/Prototypes/_Citadel/Entities/World/Debris/base_debris.yml b/Resources/Prototypes/_Citadel/Entities/World/Debris/base_debris.yml deleted file mode 100644 index 7746740b30..0000000000 --- a/Resources/Prototypes/_Citadel/Entities/World/Debris/base_debris.yml +++ /dev/null @@ -1,6 +0,0 @@ -- type: entity - id: CitadelBaseDebris - abstract: true - components: - - type: OwnedDebris - - type: LocalityLoader diff --git a/Resources/Prototypes/_Citadel/Entities/World/Debris/wrecks.yml b/Resources/Prototypes/_Citadel/Entities/World/Debris/wrecks.yml deleted file mode 100644 index d2319b2492..0000000000 --- a/Resources/Prototypes/_Citadel/Entities/World/Debris/wrecks.yml +++ /dev/null @@ -1,82 +0,0 @@ -- type: entity - id: CitadelBaseScrapDebris - parent: CitadelBaseDebris - name: Scrap Debris - abstract: true - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorTileset: - - Plating - - Plating - - Plating - - FloorSteel - - Lattice - blobDrawProb: 0.5 - radius: 6 - floorPlacements: 16 - - type: SimpleFloorPlanPopulator - entries: - Plating: - - prob: 3 # Intentional blank. - - id: SalvageMaterialCrateSpawner - prob: 1 - - id: SalvageCanisterSpawner - prob: 0.2 - - id: SalvageMobSpawner - prob: 0.7 - - id: WallSolid - prob: 1 - - id: Grille - prob: 0.5 - Lattice: - - prob: 2 - - id: Grille - prob: 0.2 - - id: SalvageMaterialCrateSpawner - prob: 0.3 - - id: SalvageCanisterSpawner - prob: 0.2 - FloorSteel: - - prob: 3 # Intentional blank. - - id: SalvageMaterialCrateSpawner - prob: 1 - - id: SalvageCanisterSpawner - prob: 0.2 - - id: SalvageMobSpawner - prob: 0.7 - - type: GCAbleObject - queue: SpaceDebris - - type: IFF - flags: HideLabel - color: "#88b0d1" - -- type: entity - id: CitadelScrapDebrisSmall - parent: CitadelBaseScrapDebris - name: Scrap Debris Small - noSpawn: true - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorPlacements: 8 - -- type: entity - id: CitadelScrapDebrisMedium - parent: CitadelBaseScrapDebris - name: Scrap Debris Medium - noSpawn: true - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorPlacements: 16 - -- type: entity - id: CitadelScrapDebrisLarge - parent: CitadelBaseScrapDebris - name: Scrap Debris Large - noSpawn: true - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorPlacements: 24 diff --git a/Resources/Prototypes/_Citadel/Entities/World/chunk.yml b/Resources/Prototypes/_Citadel/Entities/World/chunk.yml deleted file mode 100644 index c109fc715d..0000000000 --- a/Resources/Prototypes/_Citadel/Entities/World/chunk.yml +++ /dev/null @@ -1,15 +0,0 @@ -- type: entity - id: CitadelChunk - parent: MarkerBase - name: World Chunk - description: | - It's rude to stare. - It's also a bit odd you're looking at the abstract representation of the grid of reality. - noSpawn: true - components: - - type: WorldChunk - - type: Sprite - sprite: Markers/cross.rsi - netsync: false - layers: - - state: blue diff --git a/Resources/Prototypes/_Citadel/Entities/dummy.yml b/Resources/Prototypes/_Citadel/Entities/dummy.yml deleted file mode 100644 index 50fdcafe7e..0000000000 --- a/Resources/Prototypes/_Citadel/Entities/dummy.yml +++ /dev/null @@ -1,12 +0,0 @@ -- type: entity - id: CitadelDummy - parent: MarkerBase - name: Dummy - description: If you're seeing this, someone forgot some debug code. - noSpawn: true - components: - - type: Sprite - sprite: Markers/cross.rsi - netsync: false - layers: - - state: blue diff --git a/Resources/Prototypes/_Citadel/GC/world.yml b/Resources/Prototypes/_Citadel/GC/world.yml deleted file mode 100644 index b58a68158c..0000000000 --- a/Resources/Prototypes/_Citadel/GC/world.yml +++ /dev/null @@ -1,4 +0,0 @@ -- type: gcQueue - id: SpaceDebris - depth: 512 # So there's a decent bit of time before roids unload. - minDepthToProcess: 256 diff --git a/Resources/Prototypes/_Citadel/World/Biomes/basic.yml b/Resources/Prototypes/_Citadel/World/Biomes/basic.yml deleted file mode 100644 index 57aa70f1d5..0000000000 --- a/Resources/Prototypes/_Citadel/World/Biomes/basic.yml +++ /dev/null @@ -1,26 +0,0 @@ -- type: citadelBiome - id: CitadelAsteroidsStandard - priority: 0 # This probably shouldn't get selected. - noiseRanges: {} - chunkComponents: - - type: DebrisFeaturePlacerController - densityNoiseChannel: Density - - type: SimpleDebrisSelector - debrisTable: - - id: CitadelAsteroidDebrisSmall - - id: CitadelAsteroidDebrisMedium - - id: CitadelAsteroidDebrisLarge - prob: 0.7 - - id: CitadelAsteroidDebrisLarger - prob: 0.4 - - type: NoiseDrivenDebrisSelector - noiseChannel: Wreck - debrisTable: - - id: CitadelScrapDebrisSmall - - id: CitadelScrapDebrisMedium - - id: CitadelScrapDebrisLarge - prob: 0.5 - - type: NoiseRangeCarver - ranges: - - 0.4, 0.6 - noiseChannel: Carver diff --git a/Resources/Prototypes/_Citadel/World/Biomes/failsafes.yml b/Resources/Prototypes/_Citadel/World/Biomes/failsafes.yml deleted file mode 100644 index d90ade3ff0..0000000000 --- a/Resources/Prototypes/_Citadel/World/Biomes/failsafes.yml +++ /dev/null @@ -1,21 +0,0 @@ -- type: citadelBiome - id: CitadelFailsafe - priority: -999999 # This DEFINITELY shouldn't get selected! - noiseRanges: {} - -- type: citadelBiome - id: CitadelAsteroidsFallback - priority: -999998 # This probably shouldn't get selected. - noiseRanges: {} - chunkComponents: - - type: DebrisFeaturePlacerController - densityNoiseChannel: Density - - type: SimpleDebrisSelector - debrisTable: - - id: CitadelAsteroidDebrisSmall - - id: CitadelAsteroidDebrisMedium - - id: CitadelAsteroidDebrisLarge - prob: 0.7 - - id: CitadelAsteroidDebrisLarger - prob: 0.4 - diff --git a/Resources/Prototypes/_Citadel/World/noise_channels.yml b/Resources/Prototypes/_Citadel/World/noise_channels.yml deleted file mode 100644 index 668b338dd3..0000000000 --- a/Resources/Prototypes/_Citadel/World/noise_channels.yml +++ /dev/null @@ -1,44 +0,0 @@ -- type: noiseChannel - id: Density - noiseType: Perlin - fractalLacunarityByPi: 0.666666666 - remapTo0Through1: true - clippingRanges: - - 0.4, 0.6 - clippedValue: 1.658 # magic number for chunk size. - inputMultiplier: 6 # Makes density hopefully low noise in the local area while still being interesting at scale. - outputMultiplier: 50.0 # We scale density up significantly for more human-friendly numbers. - minimum: 45.0 - -- type: noiseChannel - id: DensityUnclipped - noiseType: Perlin - fractalLacunarityByPi: 0.666666666 - remapTo0Through1: true - inputMultiplier: 6 # Makes density hopefully low noise in the local area while still being interesting at scale. - outputMultiplier: 50.0 # We scale density up significantly for more human-friendly numbers. - minimum: 45.0 - -- type: noiseChannel - id: Carver - noiseType: Perlin - fractalLacunarityByPi: 0.666666666 - remapTo0Through1: true - inputMultiplier: 6 - -- type: noiseChannel - id: Wreck - noiseType: Perlin - fractalLacunarityByPi: 0.666666666 - clippingRanges: - - 0.0, 0.4 - clippedValue: 0 - remapTo0Through1: true - inputMultiplier: 16 # Makes wreck concentration very low noise at scale. - -- type: noiseChannel - id: Temperature - noiseType: Perlin - fractalLacunarityByPi: 0.666666666 - remapTo0Through1: true - inputMultiplier: 6 # Makes wreck concentration very low noise at scale. diff --git a/Resources/Prototypes/_Citadel/World/worldgen_default.yml b/Resources/Prototypes/_Citadel/World/worldgen_default.yml deleted file mode 100644 index 8b87b02627..0000000000 --- a/Resources/Prototypes/_Citadel/World/worldgen_default.yml +++ /dev/null @@ -1,9 +0,0 @@ -- type: worldgenConfig - id: Default - components: - - type: WorldController - - type: BiomeSelection - biomes: - - CitadelAsteroidsFallback - - CitadelFailsafe - - CitadelAsteroidsStandard