ExplosionSpaceTileFlood.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. using Content.Shared.Atmos;
  2. using Robust.Shared.Map;
  3. namespace Content.Server.Explosion.EntitySystems;
  4. /// <summary>
  5. /// See <see cref="ExplosionTileFlood"/>.
  6. /// </summary>
  7. public sealed class ExplosionSpaceTileFlood : ExplosionTileFlood
  8. {
  9. /// <summary>
  10. /// The keys of this dictionary correspond to space tiles that intersect a grid. The values have information
  11. /// about what grid (which could be more than one), and in what directions the space-based explosion is allowed
  12. /// to propagate from this tile.
  13. /// </summary>
  14. private Dictionary<Vector2i, BlockedSpaceTile> _gridBlockMap;
  15. /// <summary>
  16. /// After every iteration, this data set will store all the grid-tiles that were reached as a result of the
  17. /// explosion expanding in space.
  18. /// </summary>
  19. public Dictionary<EntityUid, HashSet<Vector2i>> GridJump = new();
  20. public ushort TileSize = ExplosionSystem.DefaultTileSize;
  21. public ExplosionSpaceTileFlood(ExplosionSystem system, MapCoordinates epicentre, EntityUid? referenceGrid, List<EntityUid> localGrids, float maxDistance)
  22. {
  23. (_gridBlockMap, TileSize) = system.TransformGridEdges(epicentre, referenceGrid, localGrids, maxDistance);
  24. system.GetUnblockedDirections(_gridBlockMap, TileSize);
  25. }
  26. public int AddNewTiles(int iteration, HashSet<Vector2i> inputSpaceTiles)
  27. {
  28. NewTiles = new();
  29. NewBlockedTiles = new();
  30. NewFreedTiles = new();
  31. GridJump = new();
  32. // Adjacent tiles
  33. if (TileLists.TryGetValue(iteration - 2, out var adjacent))
  34. AddNewAdjacentTiles(iteration, adjacent);
  35. if (FreedTileLists.TryGetValue((iteration - 2) % 3, out var delayedAdjacent))
  36. AddNewAdjacentTiles(iteration, delayedAdjacent);
  37. // Diagonal tiles
  38. if (TileLists.TryGetValue(iteration - 3, out var diagonal))
  39. AddNewDiagonalTiles(iteration, diagonal);
  40. if (FreedTileLists.TryGetValue((iteration - 3) % 3, out var delayedDiagonal))
  41. AddNewDiagonalTiles(iteration, delayedDiagonal);
  42. // Tiles entering space from some grid.
  43. foreach (var tile in inputSpaceTiles)
  44. {
  45. ProcessNewTile(iteration, tile, AtmosDirection.All);
  46. }
  47. // Store new tiles
  48. if (NewTiles.Count != 0)
  49. TileLists[iteration] = NewTiles;
  50. if (NewBlockedTiles.Count != 0)
  51. BlockedTileLists[iteration] = NewBlockedTiles;
  52. FreedTileLists[iteration % 3] = NewFreedTiles;
  53. // return new tile count
  54. return NewTiles.Count + NewBlockedTiles.Count;
  55. }
  56. private void JumpToGrid(BlockedSpaceTile blocker)
  57. {
  58. foreach (var edge in blocker.BlockingGridEdges)
  59. {
  60. if (edge.Grid == null) continue;
  61. if (!GridJump.TryGetValue(edge.Grid.Value, out var set))
  62. {
  63. set = new();
  64. GridJump[edge.Grid.Value] = set;
  65. }
  66. set.Add(edge.Tile);
  67. }
  68. }
  69. private void AddNewAdjacentTiles(int iteration, IEnumerable<Vector2i> tiles)
  70. {
  71. foreach (var tile in tiles)
  72. {
  73. var unblockedDirections = GetUnblockedDirectionOrAll(tile);
  74. if (unblockedDirections == AtmosDirection.Invalid)
  75. continue;
  76. for (var i = 0; i < Atmospherics.Directions; i++)
  77. {
  78. var direction = (AtmosDirection) (1 << i);
  79. if (!unblockedDirections.IsFlagSet(direction))
  80. continue; // explosion cannot propagate in this direction. Ever.
  81. ProcessNewTile(iteration, tile.Offset(direction), i.ToOppositeDir());
  82. }
  83. }
  84. }
  85. public override void InitTile(Vector2i initialTile)
  86. {
  87. ProcessedTiles.Add(initialTile);
  88. TileLists[0] = new() { initialTile };
  89. // It might be the case that the initial space-explosion tile actually overlaps on a grid. In that case we
  90. // need to manually add it to the `spaceToGridTiles` dictionary. This would normally be done automatically
  91. // during the neighbor finding steps.
  92. if (_gridBlockMap.TryGetValue(initialTile, out var blocker))
  93. JumpToGrid(blocker);
  94. }
  95. protected override void ProcessNewTile(int iteration, Vector2i tile, AtmosDirection entryDirection)
  96. {
  97. if (!_gridBlockMap.TryGetValue(tile, out var blocker))
  98. {
  99. // this tile does not intersect any grids. Add it (if its new) and continue.
  100. if (ProcessedTiles.Add(tile))
  101. NewTiles.Add(tile);
  102. return;
  103. }
  104. // Is the entry to this tile blocked?
  105. if ((blocker.UnblockedDirections & entryDirection) == 0)
  106. {
  107. // was this tile already entered from some other direction?
  108. if (EnteredBlockedTiles.Contains(tile))
  109. return;
  110. // Did the explosion already attempt to enter this tile from some other direction?
  111. if (!UnenteredBlockedTiles.Add(tile))
  112. return;
  113. // First time the explosion is reaching this tile.
  114. NewBlockedTiles.Add(tile);
  115. JumpToGrid(blocker);
  116. }
  117. // Was this tile already entered?
  118. if (!EnteredBlockedTiles.Add(tile))
  119. return;
  120. // Did the explosion already attempt to enter this tile from some other direction?
  121. if (UnenteredBlockedTiles.Contains(tile))
  122. {
  123. NewFreedTiles.Add(tile);
  124. return;
  125. }
  126. // This is a completely new tile, and we just so happened to enter it from an unblocked direction.
  127. NewTiles.Add(tile);
  128. JumpToGrid(blocker);
  129. }
  130. protected override AtmosDirection GetUnblockedDirectionOrAll(Vector2i tile)
  131. {
  132. return _gridBlockMap.TryGetValue(tile, out var blocker) ? blocker.UnblockedDirections : AtmosDirection.All;
  133. }
  134. }