DungeonJob.PostGenJunction.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. using System.Threading.Tasks;
  2. using Content.Shared.Maps;
  3. using Content.Shared.Procedural;
  4. using Content.Shared.Procedural.PostGeneration;
  5. using Content.Shared.Storage;
  6. using Robust.Shared.Map.Components;
  7. namespace Content.Server.Procedural.DungeonJob;
  8. public sealed partial class DungeonJob
  9. {
  10. /// <summary>
  11. /// <see cref="JunctionDunGen"/>
  12. /// </summary>
  13. private async Task PostGen(JunctionDunGen gen, DungeonData data, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
  14. {
  15. if (!data.Tiles.TryGetValue(DungeonDataKey.FallbackTile, out var tileProto) ||
  16. !data.SpawnGroups.TryGetValue(DungeonDataKey.Junction, out var junctionProto))
  17. {
  18. _sawmill.Error($"Dungeon data keys are missing for {nameof(gen)}");
  19. return;
  20. }
  21. var tileDef = _tileDefManager[tileProto];
  22. var entranceGroup = _prototype.Index(junctionProto);
  23. // N-wide junctions
  24. foreach (var tile in dungeon.CorridorTiles)
  25. {
  26. if (!_anchorable.TileFree(_grid, tile, DungeonSystem.CollisionLayer, DungeonSystem.CollisionMask))
  27. continue;
  28. // Check each direction:
  29. // - Check if immediate neighbors are free
  30. // - Check if the neighbors beyond that are not free
  31. // - Then check either side if they're slightly more free
  32. var exteriorWidth = (int) Math.Floor(gen.Width / 2f);
  33. var width = (int) Math.Ceiling(gen.Width / 2f);
  34. for (var i = 0; i < 2; i++)
  35. {
  36. var isValid = true;
  37. var neighborDir = (Direction) (i * 2);
  38. var neighborVec = neighborDir.ToIntVec();
  39. for (var j = -width; j <= width; j++)
  40. {
  41. if (j == 0)
  42. continue;
  43. var neighbor = tile + neighborVec * j;
  44. // If it's an end tile then check it's occupied.
  45. if (j == -width ||
  46. j == width)
  47. {
  48. if (!HasWall(neighbor))
  49. {
  50. isValid = false;
  51. break;
  52. }
  53. continue;
  54. }
  55. // If we're not at the end tile then check it + perpendicular are free.
  56. if (!_anchorable.TileFree(_grid, neighbor, DungeonSystem.CollisionLayer, DungeonSystem.CollisionMask))
  57. {
  58. isValid = false;
  59. break;
  60. }
  61. var perp1 = tile + neighborVec * j + ((Direction) ((i * 2 + 2) % 8)).ToIntVec();
  62. var perp2 = tile + neighborVec * j + ((Direction) ((i * 2 + 6) % 8)).ToIntVec();
  63. if (!_anchorable.TileFree(_grid, perp1, DungeonSystem.CollisionLayer, DungeonSystem.CollisionMask))
  64. {
  65. isValid = false;
  66. break;
  67. }
  68. if (!_anchorable.TileFree(_grid, perp2, DungeonSystem.CollisionLayer, DungeonSystem.CollisionMask))
  69. {
  70. isValid = false;
  71. break;
  72. }
  73. }
  74. if (!isValid)
  75. continue;
  76. // Check corners to see if either side opens up (if it's just a 1x wide corridor do nothing, needs to be a funnel.
  77. foreach (var j in new [] {-exteriorWidth, exteriorWidth})
  78. {
  79. var freeCount = 0;
  80. // Need at least 3 of 4 free
  81. for (var k = 0; k < 4; k++)
  82. {
  83. var cornerDir = (Direction) (k * 2 + 1);
  84. var cornerVec = cornerDir.ToIntVec();
  85. var cornerNeighbor = tile + neighborVec * j + cornerVec;
  86. if (_anchorable.TileFree(_grid, cornerNeighbor, DungeonSystem.CollisionLayer, DungeonSystem.CollisionMask))
  87. {
  88. freeCount++;
  89. }
  90. }
  91. if (freeCount < gen.Width)
  92. continue;
  93. // Valid!
  94. isValid = true;
  95. for (var x = -width + 1; x < width; x++)
  96. {
  97. var weh = tile + neighborDir.ToIntVec() * x;
  98. if (reservedTiles.Contains(weh))
  99. continue;
  100. _maps.SetTile(_gridUid, _grid, weh, _tile.GetVariantTile((ContentTileDefinition) tileDef, random));
  101. var coords = _maps.GridTileToLocal(_gridUid, _grid, weh);
  102. _entManager.SpawnEntities(coords, EntitySpawnCollection.GetSpawns(entranceGroup.Entries, random));
  103. }
  104. break;
  105. }
  106. if (isValid)
  107. {
  108. await SuspendDungeon();
  109. if (!ValidateResume())
  110. return;
  111. }
  112. break;
  113. }
  114. }
  115. }
  116. }