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:
@@ -1,386 +0,0 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Parallax.Biomes.Layers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Noise;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Parallax.Biomes;
|
||||
|
||||
public abstract class SharedBiomeSystem : EntitySystem
|
||||
{
|
||||
[Dependency] protected readonly IPrototypeManager ProtoManager = default!;
|
||||
[Dependency] private readonly ISerializationManager _serManager = default!;
|
||||
[Dependency] protected readonly ITileDefinitionManager TileDefManager = default!;
|
||||
[Dependency] private readonly TileSystem _tile = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
|
||||
protected const byte ChunkSize = 8;
|
||||
|
||||
private T Pick<T>(List<T> collection, float value)
|
||||
{
|
||||
// Listen I don't need this exact and I'm too lazy to finetune just for random ent picking.
|
||||
value %= 1f;
|
||||
value = Math.Clamp(value, 0f, 1f);
|
||||
|
||||
if (collection.Count == 1)
|
||||
return collection[0];
|
||||
|
||||
var randValue = value * collection.Count;
|
||||
|
||||
foreach (var item in collection)
|
||||
{
|
||||
randValue -= 1f;
|
||||
|
||||
if (randValue <= 0f)
|
||||
{
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
private int Pick(int count, float value)
|
||||
{
|
||||
value %= 1f;
|
||||
value = Math.Clamp(value, 0f, 1f);
|
||||
|
||||
if (count == 1)
|
||||
return 0;
|
||||
|
||||
value *= count;
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
value -= 1f;
|
||||
|
||||
if (value <= 0f)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
public bool TryGetBiomeTile(EntityUid uid, MapGridComponent grid, Vector2i indices, [NotNullWhen(true)] out Tile? tile)
|
||||
{
|
||||
if (_map.TryGetTileRef(uid, grid, indices, out var tileRef) && !tileRef.Tile.IsEmpty)
|
||||
{
|
||||
tile = tileRef.Tile;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!TryComp<BiomeComponent>(uid, out var biome))
|
||||
{
|
||||
tile = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return TryGetBiomeTile(indices, biome.Layers, biome.Seed, (uid, grid), out tile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the tile, real or otherwise, for the specified indices.
|
||||
/// </summary>
|
||||
public bool TryGetBiomeTile(Vector2i indices, List<IBiomeLayer> layers, int seed, Entity<MapGridComponent>? grid, [NotNullWhen(true)] out Tile? tile)
|
||||
{
|
||||
if (grid is { } gridEnt && _map.TryGetTileRef(gridEnt, gridEnt.Comp, indices, out var tileRef) && !tileRef.Tile.IsEmpty)
|
||||
{
|
||||
tile = tileRef.Tile;
|
||||
return true;
|
||||
}
|
||||
|
||||
return TryGetTile(indices, layers, seed, grid, out tile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the tile, real or otherwise, for the specified indices.
|
||||
/// </summary>
|
||||
[Obsolete("Use the Entity<MapGridComponent>? overload")]
|
||||
public bool TryGetBiomeTile(Vector2i indices, List<IBiomeLayer> layers, int seed, MapGridComponent? grid, [NotNullWhen(true)] out Tile? tile)
|
||||
{
|
||||
return TryGetBiomeTile(indices, layers, seed, grid == null ? null : (grid.Owner, grid), out tile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying biome tile, ignoring any existing tile that may be there.
|
||||
/// </summary>
|
||||
public bool TryGetTile(Vector2i indices, List<IBiomeLayer> layers, int seed, Entity<MapGridComponent>? grid, [NotNullWhen(true)] out Tile? tile)
|
||||
{
|
||||
for (var i = layers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var layer = layers[i];
|
||||
var noiseCopy = GetNoise(layer.Noise, seed);
|
||||
|
||||
var invert = layer.Invert;
|
||||
var value = noiseCopy.GetNoise(indices.X, indices.Y);
|
||||
value = invert ? value * -1 : value;
|
||||
|
||||
if (value < layer.Threshold)
|
||||
continue;
|
||||
|
||||
// Check if the tile is from meta layer, otherwise fall back to default layers.
|
||||
if (layer is BiomeMetaLayer meta)
|
||||
{
|
||||
if (TryGetBiomeTile(indices, ProtoManager.Index<BiomeTemplatePrototype>(meta.Template).Layers, seed, grid, out tile))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (layer is not BiomeTileLayer tileLayer)
|
||||
continue;
|
||||
|
||||
if (TryGetTile(indices, noiseCopy, tileLayer.Invert, tileLayer.Threshold, ProtoManager.Index(tileLayer.Tile), tileLayer.Variants, out tile))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
tile = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying biome tile, ignoring any existing tile that may be there.
|
||||
/// </summary>
|
||||
[Obsolete("Use the Entity<MapGridComponent>? overload")]
|
||||
public bool TryGetTile(Vector2i indices, List<IBiomeLayer> layers, int seed, MapGridComponent? grid, [NotNullWhen(true)] out Tile? tile)
|
||||
{
|
||||
return TryGetTile(indices, layers, seed, grid == null ? null : (grid.Owner, grid), out tile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying biome tile, ignoring any existing tile that may be there.
|
||||
/// </summary>
|
||||
private bool TryGetTile(Vector2i indices, FastNoiseLite noise, bool invert, float threshold, ContentTileDefinition tileDef, List<byte>? variants, [NotNullWhen(true)] out Tile? tile)
|
||||
{
|
||||
var found = noise.GetNoise(indices.X, indices.Y);
|
||||
found = invert ? found * -1 : found;
|
||||
|
||||
if (found < threshold)
|
||||
{
|
||||
tile = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
byte variant = 0;
|
||||
var variantCount = variants?.Count ?? tileDef.Variants;
|
||||
|
||||
// Pick a variant tile if they're available as well
|
||||
if (variantCount > 1)
|
||||
{
|
||||
var variantValue = (noise.GetNoise(indices.X * 8, indices.Y * 8, variantCount) + 1f) * 100;
|
||||
variant = _tile.PickVariant(tileDef, (int)variantValue);
|
||||
}
|
||||
|
||||
tile = new Tile(tileDef.TileId, variant);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the relevant entity for this tile.
|
||||
/// </summary>
|
||||
public bool TryGetEntity(Vector2i indices, BiomeComponent component, Entity<MapGridComponent>? grid,
|
||||
[NotNullWhen(true)] out string? entity)
|
||||
{
|
||||
if (!TryGetBiomeTile(indices, component.Layers, component.Seed, grid, out var tile))
|
||||
{
|
||||
entity = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return TryGetEntity(indices, component.Layers, tile.Value, component.Seed, grid, out entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the relevant entity for this tile.
|
||||
/// </summary>
|
||||
[Obsolete("Use the Entity<MapGridComponent>? overload")]
|
||||
public bool TryGetEntity(Vector2i indices, BiomeComponent component, MapGridComponent grid,
|
||||
[NotNullWhen(true)] out string? entity)
|
||||
{
|
||||
return TryGetEntity(indices, component, grid == null ? null : (grid.Owner, grid), out entity);
|
||||
}
|
||||
|
||||
public bool TryGetEntity(Vector2i indices, List<IBiomeLayer> layers, Tile tileRef, int seed, Entity<MapGridComponent>? grid,
|
||||
[NotNullWhen(true)] out string? entity)
|
||||
{
|
||||
var tileId = TileDefManager[tileRef.TypeId].ID;
|
||||
|
||||
for (var i = layers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var layer = layers[i];
|
||||
|
||||
switch (layer)
|
||||
{
|
||||
case BiomeDummyLayer:
|
||||
continue;
|
||||
case IBiomeWorldLayer worldLayer:
|
||||
if (!worldLayer.AllowedTiles.Contains(tileId))
|
||||
continue;
|
||||
|
||||
break;
|
||||
case BiomeMetaLayer:
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
var noiseCopy = GetNoise(layer.Noise, seed);
|
||||
|
||||
var invert = layer.Invert;
|
||||
var value = noiseCopy.GetNoise(indices.X, indices.Y);
|
||||
value = invert ? value * -1 : value;
|
||||
|
||||
if (value < layer.Threshold)
|
||||
continue;
|
||||
|
||||
if (layer is BiomeMetaLayer meta)
|
||||
{
|
||||
if (TryGetEntity(indices, ProtoManager.Index<BiomeTemplatePrototype>(meta.Template).Layers, tileRef, seed, grid, out entity))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Decals might block entity so need to check if there's one in front of us.
|
||||
if (layer is not BiomeEntityLayer biomeLayer)
|
||||
{
|
||||
entity = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var noiseValue = noiseCopy.GetNoise(indices.X, indices.Y, i);
|
||||
entity = Pick(biomeLayer.Entities, (noiseValue + 1f) / 2f);
|
||||
return true;
|
||||
}
|
||||
|
||||
entity = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
[Obsolete("Use the Entity<MapGridComponent>? overload")]
|
||||
public bool TryGetEntity(Vector2i indices, List<IBiomeLayer> layers, Tile tileRef, int seed, MapGridComponent grid,
|
||||
[NotNullWhen(true)] out string? entity)
|
||||
{
|
||||
return TryGetEntity(indices, layers, tileRef, seed, grid == null ? null : (grid.Owner, grid), out entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the relevant decals for this tile.
|
||||
/// </summary>
|
||||
public bool TryGetDecals(Vector2i indices, List<IBiomeLayer> layers, int seed, Entity<MapGridComponent>? grid,
|
||||
[NotNullWhen(true)] out List<(string ID, Vector2 Position)>? decals)
|
||||
{
|
||||
if (!TryGetBiomeTile(indices, layers, seed, grid, out var tileRef))
|
||||
{
|
||||
decals = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var tileId = TileDefManager[tileRef.Value.TypeId].ID;
|
||||
|
||||
for (var i = layers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var layer = layers[i];
|
||||
|
||||
// Entities might block decal so need to check if there's one in front of us.
|
||||
switch (layer)
|
||||
{
|
||||
case BiomeDummyLayer:
|
||||
continue;
|
||||
case IBiomeWorldLayer worldLayer:
|
||||
if (!worldLayer.AllowedTiles.Contains(tileId))
|
||||
continue;
|
||||
|
||||
break;
|
||||
case BiomeMetaLayer:
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
var invert = layer.Invert;
|
||||
var noiseCopy = GetNoise(layer.Noise, seed);
|
||||
var value = noiseCopy.GetNoise(indices.X, indices.Y);
|
||||
value = invert ? value * -1 : value;
|
||||
|
||||
if (value < layer.Threshold)
|
||||
continue;
|
||||
|
||||
if (layer is BiomeMetaLayer meta)
|
||||
{
|
||||
if (TryGetDecals(indices, ProtoManager.Index<BiomeTemplatePrototype>(meta.Template).Layers, seed, grid, out decals))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the other layer should even render, if not then keep going.
|
||||
if (layer is not BiomeDecalLayer decalLayer)
|
||||
{
|
||||
decals = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
decals = new List<(string ID, Vector2 Position)>();
|
||||
|
||||
for (var x = 0; x < decalLayer.Divisions; x++)
|
||||
{
|
||||
for (var y = 0; y < decalLayer.Divisions; y++)
|
||||
{
|
||||
var index = new Vector2(indices.X + x * 1f / decalLayer.Divisions, indices.Y + y * 1f / decalLayer.Divisions);
|
||||
var decalValue = noiseCopy.GetNoise(index.X, index.Y);
|
||||
decalValue = invert ? decalValue * -1 : decalValue;
|
||||
|
||||
if (decalValue < decalLayer.Threshold)
|
||||
continue;
|
||||
|
||||
decals.Add((Pick(decalLayer.Decals, (noiseCopy.GetNoise(indices.X, indices.Y, x + y * decalLayer.Divisions) + 1f) / 2f), index));
|
||||
}
|
||||
}
|
||||
|
||||
// Check other layers
|
||||
if (decals.Count == 0)
|
||||
continue;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
decals = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the relevant decals for this tile.
|
||||
/// </summary>
|
||||
[Obsolete("Use the Entity<MapGridComponent>? overload")]
|
||||
public bool TryGetDecals(Vector2i indices, List<IBiomeLayer> layers, int seed, MapGridComponent grid,
|
||||
[NotNullWhen(true)] out List<(string ID, Vector2 Position)>? decals)
|
||||
{
|
||||
return TryGetDecals(indices, layers, seed, grid == null ? null : (grid.Owner, grid), out decals);
|
||||
}
|
||||
|
||||
private FastNoiseLite GetNoise(FastNoiseLite seedNoise, int seed)
|
||||
{
|
||||
var noiseCopy = new FastNoiseLite();
|
||||
_serManager.CopyTo(seedNoise, ref noiseCopy, notNullableOverride: true);
|
||||
noiseCopy.SetSeed(noiseCopy.GetSeed() + seed);
|
||||
// Ensure re-calculate is run.
|
||||
noiseCopy.SetFractalOctaves(noiseCopy.GetFractalOctaves());
|
||||
return noiseCopy;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user