DungeonJob.DunGenNoise.cs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. using System.Numerics;
  2. using System.Threading.Tasks;
  3. using Content.Shared.Maps;
  4. using Content.Shared.Procedural;
  5. using Content.Shared.Procedural.DungeonGenerators;
  6. using Robust.Shared.Map;
  7. using Robust.Shared.Random;
  8. using Robust.Shared.Utility;
  9. namespace Content.Server.Procedural.DungeonJob;
  10. public sealed partial class DungeonJob
  11. {
  12. /// <summary>
  13. /// <see cref="NoiseDunGen"/>
  14. /// </summary>
  15. private async Task<Dungeon> GenerateNoiseDunGen(
  16. Vector2i position,
  17. NoiseDunGen dungen,
  18. HashSet<Vector2i> reservedTiles,
  19. int seed,
  20. Random random)
  21. {
  22. var tiles = new List<(Vector2i, Tile)>();
  23. var matrix = Matrix3Helpers.CreateTranslation(position);
  24. foreach (var layer in dungen.Layers)
  25. {
  26. layer.Noise.SetSeed(seed);
  27. }
  28. // First we have to find a seed tile, then floodfill from there until we get to noise
  29. // at which point we floodfill the entire noise.
  30. var iterations = dungen.Iterations;
  31. var area = new Box2i();
  32. var frontier = new Queue<Vector2i>();
  33. var rooms = new List<DungeonRoom>();
  34. var tileCount = 0;
  35. var tileCap = random.NextGaussian(dungen.TileCap, dungen.CapStd);
  36. var visited = new HashSet<Vector2i>();
  37. while (iterations > 0 && tileCount < tileCap)
  38. {
  39. var roomTiles = new HashSet<Vector2i>();
  40. iterations--;
  41. // Get a random exterior tile to start floodfilling from.
  42. var edge = random.Next(4);
  43. Vector2i seedTile;
  44. switch (edge)
  45. {
  46. case 0:
  47. seedTile = new Vector2i(random.Next(area.Left - 2, area.Right + 1), area.Bottom - 2);
  48. break;
  49. case 1:
  50. seedTile = new Vector2i(area.Right + 1, random.Next(area.Bottom - 2, area.Top + 1));
  51. break;
  52. case 2:
  53. seedTile = new Vector2i(random.Next(area.Left - 2, area.Right + 1), area.Top + 1);
  54. break;
  55. case 3:
  56. seedTile = new Vector2i(area.Left - 2, random.Next(area.Bottom - 2, area.Top + 1));
  57. break;
  58. default:
  59. throw new ArgumentOutOfRangeException();
  60. }
  61. DebugTools.Assert(!visited.Contains(seedTile));
  62. var noiseFill = false;
  63. frontier.Clear();
  64. visited.Add(seedTile);
  65. frontier.Enqueue(seedTile);
  66. area = area.UnionTile(seedTile);
  67. var roomArea = new Box2i(seedTile, seedTile + Vector2i.One);
  68. // Time to floodfill again
  69. while (frontier.TryDequeue(out var node) && tileCount < tileCap)
  70. {
  71. var foundNoise = false;
  72. foreach (var layer in dungen.Layers)
  73. {
  74. var value = layer.Noise.GetNoise(node.X, node.Y);
  75. if (value < layer.Threshold)
  76. continue;
  77. foundNoise = true;
  78. noiseFill = true;
  79. // Still want the tile to gen as normal but can't do anything with it.
  80. if (reservedTiles.Contains(node))
  81. break;
  82. roomArea = roomArea.UnionTile(node);
  83. var tileDef = _tileDefManager[layer.Tile];
  84. var variant = _tile.PickVariant((ContentTileDefinition) tileDef, random);
  85. var adjusted = Vector2.Transform(node + _grid.TileSizeHalfVector, matrix).Floored();
  86. tiles.Add((adjusted, new Tile(tileDef.TileId, variant: variant)));
  87. roomTiles.Add(adjusted);
  88. tileCount++;
  89. break;
  90. }
  91. // Don't get neighbors if they don't have noise.
  92. // only if we've already found any noise.
  93. if (noiseFill && !foundNoise)
  94. continue;
  95. for (var x = -1; x <= 1; x++)
  96. {
  97. for (var y = -1; y <= 1; y++)
  98. {
  99. // Cardinals only
  100. if (x != 0 && y != 0)
  101. continue;
  102. var neighbor = new Vector2i(node.X + x, node.Y + y);
  103. if (!visited.Add(neighbor))
  104. continue;
  105. area = area.UnionTile(neighbor);
  106. frontier.Enqueue(neighbor);
  107. }
  108. }
  109. await SuspendIfOutOfTime();
  110. ValidateResume();
  111. }
  112. var center = Vector2.Zero;
  113. foreach (var tile in roomTiles)
  114. {
  115. center += tile + _grid.TileSizeHalfVector;
  116. }
  117. center /= roomTiles.Count;
  118. rooms.Add(new DungeonRoom(roomTiles, center, roomArea, new HashSet<Vector2i>()));
  119. await SuspendIfOutOfTime();
  120. ValidateResume();
  121. }
  122. _maps.SetTiles(_gridUid, _grid, tiles);
  123. var dungeon = new Dungeon(rooms);
  124. return dungeon;
  125. }
  126. }