DungeonJob.DunGenNoiseDistance.cs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. using System.Numerics;
  2. using System.Threading.Tasks;
  3. using Content.Shared.Maps;
  4. using Content.Shared.Procedural;
  5. using Content.Shared.Procedural.Distance;
  6. using Content.Shared.Procedural.DungeonGenerators;
  7. using Robust.Shared.Map;
  8. namespace Content.Server.Procedural.DungeonJob;
  9. public sealed partial class DungeonJob
  10. {
  11. /*
  12. * See https://www.redblobgames.com/maps/terrain-from-noise/#islands
  13. * Really it's just blending from the original noise (which may occupy the entire area)
  14. * with some other shape to confine it into a bounds more naturally.
  15. * https://old.reddit.com/r/proceduralgeneration/comments/kaen7h/new_video_on_procedural_island_noise_generation/gfjmgen/ also has more variations
  16. */
  17. /// <summary>
  18. /// <see cref="NoiseDistanceDunGen"/>
  19. /// </summary>
  20. private async Task<Dungeon> GenerateNoiseDistanceDunGen(
  21. Vector2i position,
  22. NoiseDistanceDunGen dungen,
  23. HashSet<Vector2i> reservedTiles,
  24. int seed,
  25. Random random)
  26. {
  27. var tiles = new List<(Vector2i, Tile)>();
  28. var matrix = Matrix3Helpers.CreateTranslation(position);
  29. foreach (var layer in dungen.Layers)
  30. {
  31. layer.Noise.SetSeed(seed);
  32. }
  33. // First we have to find a seed tile, then floodfill from there until we get to noise
  34. // at which point we floodfill the entire noise.
  35. var area = Box2i.FromDimensions(-dungen.Size / 2, dungen.Size);
  36. var roomTiles = new HashSet<Vector2i>();
  37. var width = (float) area.Width;
  38. var height = (float) area.Height;
  39. for (var x = area.Left; x <= area.Right; x++)
  40. {
  41. for (var y = area.Bottom; y <= area.Top; y++)
  42. {
  43. var node = new Vector2i(x, y);
  44. foreach (var layer in dungen.Layers)
  45. {
  46. var value = layer.Noise.GetNoise(node.X, node.Y);
  47. if (dungen.DistanceConfig != null)
  48. {
  49. // Need to get dx - dx in a range from -1 -> 1
  50. var dx = 2 * x / width;
  51. var dy = 2 * y / height;
  52. var distance = GetDistance(dx, dy, dungen.DistanceConfig);
  53. value = MathHelper.Lerp(value, 1f - distance, dungen.DistanceConfig.BlendWeight);
  54. }
  55. if (value < layer.Threshold)
  56. continue;
  57. var tileDef = _tileDefManager[layer.Tile];
  58. var variant = _tile.PickVariant((ContentTileDefinition) tileDef, random);
  59. var adjusted = Vector2.Transform(node + _grid.TileSizeHalfVector, matrix).Floored();
  60. // Do this down here because noise has a much higher chance of failing than reserved tiles.
  61. if (reservedTiles.Contains(adjusted))
  62. {
  63. break;
  64. }
  65. tiles.Add((adjusted, new Tile(tileDef.TileId, variant: variant)));
  66. roomTiles.Add(adjusted);
  67. break;
  68. }
  69. }
  70. await SuspendDungeon();
  71. }
  72. var room = new DungeonRoom(roomTiles, area.Center, area, new HashSet<Vector2i>());
  73. _maps.SetTiles(_gridUid, _grid, tiles);
  74. var dungeon = new Dungeon(new List<DungeonRoom>()
  75. {
  76. room,
  77. });
  78. await SuspendDungeon();
  79. return dungeon;
  80. }
  81. private float GetDistance(float dx, float dy, IDunGenDistance distance)
  82. {
  83. switch (distance)
  84. {
  85. case DunGenEuclideanSquaredDistance:
  86. return MathF.Min(1f, (dx * dx + dy * dy) / MathF.Sqrt(2));
  87. case DunGenSquareBump:
  88. return 1f - (1f - dx * dx) * (1f - dy * dy);
  89. default:
  90. throw new ArgumentOutOfRangeException();
  91. }
  92. }
  93. }