1
0

DungeonJob.PostGenExternalWindow.cs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. using System.Linq;
  2. using System.Threading.Tasks;
  3. using Content.Shared.Maps;
  4. using Content.Shared.Procedural;
  5. using Content.Shared.Procedural.PostGeneration;
  6. using Content.Shared.Storage;
  7. using Robust.Shared.Map;
  8. using Robust.Shared.Map.Components;
  9. using Robust.Shared.Random;
  10. namespace Content.Server.Procedural.DungeonJob;
  11. public sealed partial class DungeonJob
  12. {
  13. // (Comment refers to internal & external).
  14. /*
  15. * You may be wondering why these are different.
  16. * It's because for internals we want to force it as it looks nicer and not leave it up to chance.
  17. */
  18. // TODO: Can probably combine these a bit, their differences are in really annoying to pull out spots.
  19. /// <summary>
  20. /// <see cref="ExternalWindowDunGen"/>
  21. /// </summary>
  22. private async Task PostGen(ExternalWindowDunGen gen, DungeonData data, Dungeon dungeon, HashSet<Vector2i> reservedTiles, Random random)
  23. {
  24. if (!data.Tiles.TryGetValue(DungeonDataKey.FallbackTile, out var tileProto) ||
  25. !data.SpawnGroups.TryGetValue(DungeonDataKey.Window, out var windowGroup))
  26. {
  27. _sawmill.Error($"Unable to get dungeon data for {nameof(gen)}");
  28. return;
  29. }
  30. // Iterate every tile with N chance to spawn windows on that wall per cardinal dir.
  31. var chance = 0.25 / 3f;
  32. var allExterior = new HashSet<Vector2i>(dungeon.CorridorExteriorTiles);
  33. allExterior.UnionWith(dungeon.RoomExteriorTiles);
  34. var validTiles = allExterior.ToList();
  35. random.Shuffle(validTiles);
  36. var tiles = new List<(Vector2i, Tile)>();
  37. var tileDef = _tileDefManager[tileProto];
  38. var count = Math.Floor(validTiles.Count * chance);
  39. var index = 0;
  40. var takenTiles = new HashSet<Vector2i>();
  41. // There's a bunch of shit here but tl;dr
  42. // - don't spawn over cap
  43. // - Check if we have 3 tiles in a row that aren't corners and aren't obstructed
  44. foreach (var tile in validTiles)
  45. {
  46. if (index > count)
  47. break;
  48. // Room tile / already used.
  49. if (!_anchorable.TileFree(_grid, tile, DungeonSystem.CollisionLayer, DungeonSystem.CollisionMask) ||
  50. takenTiles.Contains(tile))
  51. {
  52. continue;
  53. }
  54. // Check we're not on a corner
  55. for (var i = 0; i < 2; i++)
  56. {
  57. var dir = (Direction) (i * 2);
  58. var dirVec = dir.ToIntVec();
  59. var isValid = true;
  60. // Check 1 beyond either side to ensure it's not a corner.
  61. for (var j = -1; j < 4; j++)
  62. {
  63. var neighbor = tile + dirVec * j;
  64. if (!allExterior.Contains(neighbor) ||
  65. takenTiles.Contains(neighbor) ||
  66. !_anchorable.TileFree(_grid, neighbor, DungeonSystem.CollisionLayer, DungeonSystem.CollisionMask))
  67. {
  68. isValid = false;
  69. break;
  70. }
  71. // Also check perpendicular that it is free
  72. foreach (var k in new [] {2, 6})
  73. {
  74. var perp = (Direction) ((i * 2 + k) % 8);
  75. var perpVec = perp.ToIntVec();
  76. var perpTile = tile + perpVec;
  77. if (allExterior.Contains(perpTile) ||
  78. takenTiles.Contains(neighbor) ||
  79. !_anchorable.TileFree(_grid, perpTile, DungeonSystem.CollisionLayer, DungeonSystem.CollisionMask))
  80. {
  81. isValid = false;
  82. break;
  83. }
  84. }
  85. if (!isValid)
  86. break;
  87. }
  88. if (!isValid)
  89. continue;
  90. for (var j = 0; j < 3; j++)
  91. {
  92. var neighbor = tile + dirVec * j;
  93. if (reservedTiles.Contains(neighbor))
  94. continue;
  95. tiles.Add((neighbor, _tile.GetVariantTile((ContentTileDefinition) tileDef, random)));
  96. index++;
  97. takenTiles.Add(neighbor);
  98. }
  99. }
  100. }
  101. _maps.SetTiles(_gridUid, _grid, tiles);
  102. index = 0;
  103. var spawnEntry = _prototype.Index(windowGroup);
  104. foreach (var tile in tiles)
  105. {
  106. var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile.Item1);
  107. index += spawnEntry.Entries.Count;
  108. _entManager.SpawnEntities(gridPos, EntitySpawnCollection.GetSpawns(spawnEntry.Entries, random));
  109. await SuspendDungeon();
  110. if (!ValidateResume())
  111. return;
  112. }
  113. }
  114. }