Biome rework (#37735)

* DungeonData rework

Back to fields, serializes better, just make new layers dumby.

* wawawewa

* Fix this

* Fixes

* Port the work over

* wawawewa

* zoom

* Kinda workin

* Adjust wawa

* Unloading work

* Ore + entitytable fixes

Iterate every dungeon not just last.

* Big shot

* wawawewa

* Fixes

* true

* Fixes

# Conflicts:
#	Content.Server/Procedural/DungeonJob/DungeonJob.cs

* wawawewa

* Fixes

* Fix

* Lot of work

* wawawewa

* Fixing

* eh?

* a

* Fix a heap of stuff

* Better ignored check

* Reserve tile changes

* biome

* changes

* wawawewa

* Fixes & snow

* Shadow fixes

* wawawewa

* smol

* Add layer API

* More work

* wawawewa

* Preloads and running again

* wawawewa

* Modified

* Replacements and command

* Runtime support

* werk

* Fix expeds + dungeon alltiles

* reh

---------

Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com>
This commit is contained in:
metalgearsloth
2025-07-03 14:36:06 +10:00
committed by GitHub
parent fdf3d6715e
commit fe7b96147c
116 changed files with 3005 additions and 3537 deletions

View File

@@ -0,0 +1,12 @@
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration;
/// <summary>
/// Runs cables throughout the dungeon.
/// </summary>
public sealed partial class AutoCablingDunGen : IDunGenLayer
{
[DataField(required: true)]
public EntProtoId Entity;
}

View File

@@ -0,0 +1,35 @@
using Content.Shared.Maps;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration;
/// <summary>
/// Iterates room edges and places the relevant tiles and walls on any free indices.
/// </summary>
/// <remarks>
/// Dungeon data keys are:
/// - CornerWalls (Optional)
/// - FallbackTile
/// - Walls
/// </remarks>
public sealed partial class BoundaryWallDunGen : IDunGenLayer
{
[DataField]
public BoundaryWallFlags Flags = BoundaryWallFlags.Corridors | BoundaryWallFlags.Rooms;
[DataField(required: true)]
public EntProtoId Wall;
[DataField]
public EntProtoId? CornerWall;
[DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile;
}
[Flags]
public enum BoundaryWallFlags : byte
{
Rooms = 1 << 0,
Corridors = 1 << 1,
}

View File

@@ -0,0 +1,17 @@
using Content.Shared.EntityTable;
using Content.Shared.Storage;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration;
/// <summary>
/// Spawns entities inside corners.
/// </summary>
public sealed partial class CornerClutterDunGen : IDunGenLayer
{
[DataField]
public float Chance = 0.50f;
[DataField(required:true)]
public ProtoId<EntityTablePrototype> Contents = new();
}

View File

@@ -0,0 +1,20 @@
using Content.Shared.EntityTable;
using Content.Shared.Storage;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration;
/// <summary>
/// Adds entities randomly to the corridors.
/// </summary>
public sealed partial class CorridorClutterDunGen : IDunGenLayer
{
[DataField]
public float Chance = 0.05f;
/// <summary>
/// The default starting bulbs
/// </summary>
[DataField(required: true)]
public ProtoId<EntityTablePrototype> Contents;
}

View File

@@ -0,0 +1,31 @@
namespace Content.Shared.Procedural.PostGeneration;
/// <summary>
/// Applies decal skirting to corridors.
/// </summary>
public sealed partial class CorridorDecalSkirtingDunGen : IDunGenLayer
{
/// <summary>
/// Decal where 1 edge is found.
/// </summary>
[DataField]
public Dictionary<DirectionFlag, string> CardinalDecals = new();
/// <summary>
/// Decal where 1 corner edge is found.
/// </summary>
[DataField]
public Dictionary<Direction, string> PocketDecals = new();
/// <summary>
/// Decal where 2 or 3 edges are found.
/// </summary>
[DataField]
public Dictionary<DirectionFlag, string> CornerDecals = new();
/// <summary>
/// Optional color to apply to the decals.
/// </summary>
[DataField]
public Color? Color;
}

View File

@@ -0,0 +1,28 @@
using Content.Shared.Maps;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration;
/// <summary>
/// Connects room entrances via corridor segments.
/// </summary>
public sealed partial class CorridorDunGen : IDunGenLayer
{
/// <summary>
/// How far we're allowed to generate a corridor before calling it.
/// </summary>
/// <remarks>
/// Given the heavy weightings this needs to be fairly large for larger dungeons.
/// </remarks>
[DataField]
public int PathLimit = 2048;
/// <summary>
/// How wide to make the corridor.
/// </summary>
[DataField]
public float Width = 3f;
[DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile;
}

View File

@@ -0,0 +1,24 @@
using Content.Shared.EntityTable;
using Content.Shared.Maps;
using Content.Shared.Storage;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration;
/// <summary>
/// Selects [count] rooms and places external doors to them.
/// </summary>
public sealed partial class DungeonEntranceDunGen : IDunGenLayer
{
/// <summary>
/// How many rooms we place doors on.
/// </summary>
[DataField]
public int Count = 1;
[DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile;
[DataField(required: true)]
public ProtoId<EntityTablePrototype> Contents;
}

View File

@@ -0,0 +1,18 @@
using Content.Shared.EntityTable;
using Content.Shared.Maps;
using Content.Shared.Storage;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration;
/// <summary>
/// Spawns entities on either side of an entrance.
/// </summary>
public sealed partial class EntranceFlankDunGen : IDunGenLayer
{
[DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile;
[DataField(required: true)]
public ProtoId<EntityTablePrototype> Contents = new();
}

View File

@@ -0,0 +1,17 @@
using Content.Shared.EntityTable;
using Content.Shared.Maps;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration;
/// <summary>
/// If external areas are found will try to generate windows.
/// </summary>
public sealed partial class ExternalWindowDunGen : IDunGenLayer
{
[DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile;
[DataField(required: true)]
public ProtoId<EntityTablePrototype> Contents;
}

View File

@@ -1,4 +1,7 @@
using System.Numerics;
using Content.Shared.Maps;
using Content.Shared.Procedural.Distance;
using Robust.Shared.Noise;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.DungeonLayers;
@@ -6,10 +9,6 @@ namespace Content.Shared.Procedural.DungeonLayers;
/// <summary>
/// Fills unreserved tiles with the specified entity prototype.
/// </summary>
/// <remarks>
/// DungeonData keys are:
/// - Fill
/// </remarks>
public sealed partial class FillGridDunGen : IDunGenLayer
{
/// <summary>
@@ -20,4 +19,29 @@ public sealed partial class FillGridDunGen : IDunGenLayer
[DataField(required: true)]
public EntProtoId Entity;
#region Noise
[DataField]
public bool Invert;
/// <summary>
/// Optionally don't spawn entities if the noise value matches.
/// </summary>
[DataField]
public FastNoiseLite? ReservedNoise;
/// <summary>
/// Noise threshold for <see cref="ReservedNoise"/>. Does nothing without it.
/// </summary>
[DataField]
public float Threshold = -1f;
[DataField]
public IDunGenDistance? DistanceConfig;
[DataField]
public Vector2 Size;
#endregion
}

View File

@@ -0,0 +1,17 @@
using Content.Shared.EntityTable;
using Content.Shared.Maps;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration;
/// <summary>
/// If internal areas are found will try to generate windows.
/// </summary>
public sealed partial class InternalWindowDunGen : IDunGenLayer
{
[DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile;
[DataField(required: true)]
public ProtoId<EntityTablePrototype> Contents;
}

View File

@@ -0,0 +1,24 @@
using Content.Shared.EntityTable;
using Content.Shared.Maps;
using Content.Shared.Storage;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration;
/// <summary>
/// Places the specified entities at junction areas.
/// </summary>
public sealed partial class JunctionDunGen : IDunGenLayer
{
/// <summary>
/// Width to check for junctions.
/// </summary>
[DataField]
public int Width = 3;
[DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile;
[DataField(required: true)]
public ProtoId<EntityTablePrototype> Contents;
}

View File

@@ -0,0 +1,33 @@
using Content.Shared.EntityTable;
using Content.Shared.Maps;
using Content.Shared.Storage;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration;
/// <summary>
/// Places the specified entities on the middle connections between rooms
/// </summary>
public sealed partial class MiddleConnectionDunGen : IDunGenLayer
{
/// <summary>
/// How much overlap there needs to be between 2 rooms exactly.
/// </summary>
[DataField]
public int OverlapCount = -1;
/// <summary>
/// How many connections to spawn between rooms.
/// </summary>
[DataField]
public int Count = 1;
[DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile;
[DataField(required: true)]
public ProtoId<EntityTablePrototype> Contents;
[DataField]
public ProtoId<EntityTablePrototype>? Flank;
}

View File

@@ -1,10 +1,8 @@
using Content.Shared.EntityTable;
using Content.Shared.Storage;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.DungeonLayers;
/// <summary>
/// Spawns mobs inside of the dungeon randomly.
/// </summary>

View File

@@ -0,0 +1,15 @@
using Robust.Shared.Noise;
namespace Content.Shared.Procedural.DungeonLayers;
/// <summary>
/// Sets tiles as rooved.
/// </summary>
public sealed partial class RoofDunGen : IDunGenLayer
{
[DataField]
public float Threshold = -1f;
[DataField]
public FastNoiseLite? Noise;
}

View File

@@ -0,0 +1,17 @@
using Content.Shared.EntityTable;
using Content.Shared.Maps;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration;
/// <summary>
/// Places tiles / entities onto room entrances.
/// </summary>
public sealed partial class RoomEntranceDunGen : IDunGenLayer
{
[DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile;
[DataField]
public ProtoId<EntityTablePrototype> Contents;
}

View File

@@ -0,0 +1,36 @@
using Content.Shared.Decals;
using Content.Shared.Maps;
using Robust.Shared.Noise;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared.Procedural.DungeonLayers;
public sealed partial class SampleDecalDunGen : IDunGenLayer
{
/// <summary>
/// Reserve any tiles we update.
/// </summary>
[DataField]
public bool ReserveTiles = true;
[DataField(customTypeSerializer:typeof(PrototypeIdListSerializer<ContentTileDefinition>))]
public List<string> AllowedTiles { get; private set; } = new();
/// <summary>
/// Divide each tile up by this amount.
/// </summary>
[DataField]
public float Divisions = 1f;
[DataField]
public FastNoiseLite Noise { get; private set; } = new(0);
[DataField]
public float Threshold { get; private set; } = 0.8f;
[DataField] public bool Invert { get; private set; } = false;
[DataField(required: true)]
public List<ProtoId<DecalPrototype>> Decals = new();
}

View File

@@ -0,0 +1,31 @@
using Content.Shared.Maps;
using Robust.Shared.Noise;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared.Procedural.DungeonLayers;
/// <summary>
/// Samples noise to spawn the specified entity
/// </summary>
public sealed partial class SampleEntityDunGen : IDunGenLayer
{
/// <summary>
/// Reserve any tiles we update.
/// </summary>
[DataField]
public bool ReserveTiles = true;
[DataField(customTypeSerializer:typeof(PrototypeIdListSerializer<ContentTileDefinition>))]
public List<string> AllowedTiles { get; private set; } = new();
[DataField] public FastNoiseLite Noise { get; private set; } = new(0);
[DataField]
public float Threshold { get; private set; } = 0.5f;
[DataField] public bool Invert { get; private set; } = false;
[DataField]
public List<EntProtoId> Entities = new();
}

View File

@@ -0,0 +1,35 @@
using Content.Shared.Maps;
using Robust.Shared.Noise;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared.Procedural.DungeonLayers;
/// <summary>
/// Samples noise and spawns the specified tile in the dungeon area.
/// </summary>
[Serializable, NetSerializable]
public sealed partial class SampleTileDunGen : IDunGenLayer
{
/// <summary>
/// Reserve any tiles we update.
/// </summary>
[DataField]
public bool ReserveTiles = true;
[DataField] public FastNoiseLite Noise { get; private set; } = new(0);
[DataField]
public float Threshold { get; private set; } = 0.5f;
[DataField] public bool Invert { get; private set; } = false;
/// <summary>
/// Which tile variants to use for this layer. Uses all of the tile's variants if none specified
/// </summary>
[DataField]
public List<byte>? Variants = null;
[DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile = string.Empty;
}

View File

@@ -0,0 +1,28 @@
using Content.Shared.Maps;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.DungeonLayers;
/// <summary>
/// Connects dungeons via points that get subdivided.
/// </summary>
public sealed partial class SplineDungeonConnectorDunGen : IDunGenLayer
{
[DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile;
[DataField]
public ProtoId<ContentTileDefinition>? WidenTile;
/// <summary>
/// Will divide the distance between the start and end points so that no subdivision is more than these metres away.
/// </summary>
[DataField]
public int DivisionDistance = 20;
/// <summary>
/// How much each subdivision can vary from the middle.
/// </summary>
[DataField]
public float VarianceMax = 0.15f;
}

View File

@@ -0,0 +1,24 @@
using Content.Shared.EntityTable;
using Content.Shared.Maps;
using Content.Shared.Storage;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration;
/// <summary>
/// Spawns on the boundary tiles of rooms.
/// </summary>
public sealed partial class WallMountDunGen : IDunGenLayer
{
/// <summary>
/// Chance per free tile to spawn a wallmount.
/// </summary>
[DataField]
public double Prob = 0.1;
[DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile;
[DataField(required: true)]
public ProtoId<EntityTablePrototype> Contents;
}

View File

@@ -0,0 +1,41 @@
using Content.Shared.Maps;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.PostGeneration;
// Ime a worm
/// <summary>
/// Generates worm corridors.
/// </summary>
public sealed partial class WormCorridorDunGen : IDunGenLayer
{
[DataField]
public int PathLimit = 2048;
/// <summary>
/// How many times to run the worm
/// </summary>
[DataField]
public int Count = 20;
/// <summary>
/// How long to make each worm
/// </summary>
[DataField]
public int Length = 20;
/// <summary>
/// Maximum amount the angle can change in a single step.
/// </summary>
[DataField]
public Angle MaxAngleChange = Angle.FromDegrees(45);
/// <summary>
/// How wide to make the corridor.
/// </summary>
[DataField]
public float Width = 3f;
[DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile;
}