Files
tbd-station-14/Content.Server/Procedural/DungeonJob/DungeonJob.DunGenNoiseDistance.cs
metalgearsloth a2f99cc69e VGRoid support (#27659)
* Dungeon spawn support for grid spawns

* Recursive dungeons working

* Mask approach working

* zack

* More work

* Fix recursive dungeons

* Heap of work

* weh

* the cud

* rar

* Job

* weh

* weh

* weh

* Master merges

* orch

* weh

* vgroid most of the work

* Tweaks

* Tweaks

* weh

* do do do do do do

* Basic layout

* Ore spawning working

* Big breaking changes

* Mob gen working

* weh

* Finalising

* emo

* More finalising

* reverty

* Reduce distance
2024-07-03 22:23:11 +10:00

113 lines
3.9 KiB
C#

using System.Numerics;
using System.Threading.Tasks;
using Content.Shared.Maps;
using Content.Shared.Procedural;
using Content.Shared.Procedural.Distance;
using Content.Shared.Procedural.DungeonGenerators;
using Robust.Shared.Map;
namespace Content.Server.Procedural.DungeonJob;
public sealed partial class DungeonJob
{
/*
* See https://www.redblobgames.com/maps/terrain-from-noise/#islands
* Really it's just blending from the original noise (which may occupy the entire area)
* with some other shape to confine it into a bounds more naturally.
* https://old.reddit.com/r/proceduralgeneration/comments/kaen7h/new_video_on_procedural_island_noise_generation/gfjmgen/ also has more variations
*/
/// <summary>
/// <see cref="NoiseDistanceDunGen"/>
/// </summary>
private async Task<Dungeon> GenerateNoiseDistanceDunGen(
Vector2i position,
NoiseDistanceDunGen dungen,
HashSet<Vector2i> reservedTiles,
int seed,
Random random)
{
var tiles = new List<(Vector2i, Tile)>();
var matrix = Matrix3Helpers.CreateTranslation(position);
foreach (var layer in dungen.Layers)
{
layer.Noise.SetSeed(seed);
}
// First we have to find a seed tile, then floodfill from there until we get to noise
// at which point we floodfill the entire noise.
var area = Box2i.FromDimensions(-dungen.Size / 2, dungen.Size);
var roomTiles = new HashSet<Vector2i>();
var width = (float) area.Width;
var height = (float) area.Height;
for (var x = area.Left; x <= area.Right; x++)
{
for (var y = area.Bottom; y <= area.Top; y++)
{
var node = new Vector2i(x, y);
foreach (var layer in dungen.Layers)
{
var value = layer.Noise.GetNoise(node.X, node.Y);
if (dungen.DistanceConfig != null)
{
// Need to get dx - dx in a range from -1 -> 1
var dx = 2 * x / width;
var dy = 2 * y / height;
var distance = GetDistance(dx, dy, dungen.DistanceConfig);
value = MathHelper.Lerp(value, 1f - distance, dungen.DistanceConfig.BlendWeight);
}
if (value < layer.Threshold)
continue;
var tileDef = _tileDefManager[layer.Tile];
var variant = _tile.PickVariant((ContentTileDefinition) tileDef, random);
var adjusted = Vector2.Transform(node + _grid.TileSizeHalfVector, matrix).Floored();
// Do this down here because noise has a much higher chance of failing than reserved tiles.
if (reservedTiles.Contains(adjusted))
{
break;
}
tiles.Add((adjusted, new Tile(tileDef.TileId, variant: variant)));
roomTiles.Add(adjusted);
break;
}
}
await SuspendDungeon();
}
var room = new DungeonRoom(roomTiles, area.Center, area, new HashSet<Vector2i>());
_maps.SetTiles(_gridUid, _grid, tiles);
var dungeon = new Dungeon(new List<DungeonRoom>()
{
room,
});
await SuspendDungeon();
return dungeon;
}
private float GetDistance(float dx, float dy, IDunGenDistance distance)
{
switch (distance)
{
case DunGenEuclideanSquaredDistance:
return MathF.Min(1f, (dx * dx + dy * dy) / MathF.Sqrt(2));
case DunGenSquareBump:
return 1f - (1f - dx * dx) * (1f - dy * dy);
default:
throw new ArgumentOutOfRangeException();
}
}
}