using Content.Shared.Atmos; namespace Content.Server.Explosion.EntitySystems; /// /// This is the base class for and . It just exists to avoid some code duplication, because those classes are generally quite distinct. /// internal abstract class TileExplosion { // Main tile data sets, mapping iterations onto tile lists internal Dictionary> TileLists = new(); protected Dictionary> BlockedTileLists = new(); protected Dictionary> FreedTileLists = new(); // The new tile lists added each iteration. I **could** just pass these along to every function, but IMO it is more // readable if they are just private variables. protected List NewTiles = default!; protected List NewBlockedTiles = default!; protected HashSet NewFreedTiles = default!; // HashSets used to ensure uniqueness of tiles. Prevents the explosion from looping back in on itself. protected HashSet ProcessedTiles = new(); protected HashSet UnenteredBlockedTiles = new(); protected HashSet EnteredBlockedTiles = new(); internal virtual void InitTile(Vector2i initialTile) { ProcessedTiles.Add(initialTile); TileLists[0] = new() { initialTile }; } protected abstract void ProcessNewTile(int iteration, Vector2i tile, AtmosDirection entryDirections); protected abstract AtmosDirection GetUnblockedDirectionOrAll(Vector2i tile); protected void AddNewDiagonalTiles(int iteration, IEnumerable tiles, bool ignoreLocalBlocker = false) { AtmosDirection entryDirection = AtmosDirection.Invalid; foreach (var tile in tiles) { var freeDirections = ignoreLocalBlocker ? AtmosDirection.All : GetUnblockedDirectionOrAll(tile); // Get the free directions of the directly adjacent tiles var freeDirectionsN = GetUnblockedDirectionOrAll(tile.Offset(AtmosDirection.North)); var freeDirectionsE = GetUnblockedDirectionOrAll(tile.Offset(AtmosDirection.East)); var freeDirectionsS = GetUnblockedDirectionOrAll(tile.Offset(AtmosDirection.South)); var freeDirectionsW = GetUnblockedDirectionOrAll(tile.Offset(AtmosDirection.West)); // North East if (freeDirections.IsFlagSet(AtmosDirection.North) && freeDirectionsN.IsFlagSet(AtmosDirection.SouthEast)) entryDirection |= AtmosDirection.West; if (freeDirections.IsFlagSet(AtmosDirection.East) && freeDirectionsE.IsFlagSet(AtmosDirection.NorthWest)) entryDirection |= AtmosDirection.South; if (entryDirection != AtmosDirection.Invalid) { ProcessNewTile(iteration, tile + (1, 1), entryDirection); entryDirection = AtmosDirection.Invalid; } // North West if (freeDirections.IsFlagSet(AtmosDirection.North) && freeDirectionsN.IsFlagSet(AtmosDirection.SouthWest)) entryDirection |= AtmosDirection.East; if (freeDirections.IsFlagSet(AtmosDirection.West) && freeDirectionsW.IsFlagSet(AtmosDirection.NorthEast)) entryDirection |= AtmosDirection.West; if (entryDirection != AtmosDirection.Invalid) { ProcessNewTile(iteration, tile + (-1, 1), entryDirection); entryDirection = AtmosDirection.Invalid; } // South East if (freeDirections.IsFlagSet(AtmosDirection.South) && freeDirectionsS.IsFlagSet(AtmosDirection.NorthEast)) entryDirection |= AtmosDirection.West; if (freeDirections.IsFlagSet(AtmosDirection.East) && freeDirectionsE.IsFlagSet(AtmosDirection.SouthWest)) entryDirection |= AtmosDirection.North; if (entryDirection != AtmosDirection.Invalid) { ProcessNewTile(iteration, tile + (1, -1), entryDirection); entryDirection = AtmosDirection.Invalid; } // South West if (freeDirections.IsFlagSet(AtmosDirection.South) && freeDirectionsS.IsFlagSet(AtmosDirection.NorthWest)) entryDirection |= AtmosDirection.West; if (freeDirections.IsFlagSet(AtmosDirection.West) && freeDirectionsW.IsFlagSet(AtmosDirection.SouthEast)) entryDirection |= AtmosDirection.North; if (entryDirection != AtmosDirection.Invalid) { ProcessNewTile(iteration, tile + (-1, -1), entryDirection); entryDirection = AtmosDirection.Invalid; } } } /// /// Merge all tile lists into a single output tile list. /// internal void CleanUp() { foreach (var (iteration, blocked) in BlockedTileLists) { if (TileLists.TryGetValue(iteration, out var tiles)) tiles.AddRange(blocked); else TileLists[iteration] = blocked; } } }