Files
tbd-station-14/Content.Server/Explosion/EntitySystems/SpaceExplosion.cs
Leon Friedrich 56168e592e Explosion refactor (#5230)
* Explosions

* fix yaml typo

and prevent silly UI inputs

* oop

* Use modified contains() checks

And remove IEnumerable

* Buff nuke, nerf meteors

* optimize the entity lookup stuff a bit

* fix tile (0,0) error

forgot to do an initial Enumerator.MoveNext(), so the first tile was always the "null" tile.

* remove celebration

* byte -> int

* remove diag edge tile dict

* fix one bug

but there is another

* fix the other bug

turns out dividing a ushort leads to rounding errors.  Why TF is the grid tile size even a ushort in the first place.

* improve edge map

* fix minor bug

If the initial-explosion tile had an airtight entity on it, the tile was processed twice.

* some reviews (transform queries, eye.mapid, and tilesizes in overlays)

* Apply suggestions from code review

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>

* is map paused

* GetAllTiles ignores space by default

* WriteLine -> WriteError

* First -> FirstOrDefault()

* default prototype const string

* entity query

* misc review changes

* grid edge max distance

* fix fire texture defn

bad use of type serializer and ioc-resolves

* Remove ExplosionLaunched

And allow nukes to throw items towards the outer part of an explosion

* no hot-reload disclaimer

* replace prototype id string with int index

* optimise damage a tiiiiny bit.

* entity queries

* comments

* misc mirror comments

* cvars

* admin logs

* move intensity-per-state to prototype

* update tile event to ECS event

* git mv

* Tweak rpg & minibomb

also fix merge bug

* you don't exist anymore go away

* Fix build

Co-authored-by: moonheart08 <moonheart08@users.noreply.github.com>
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
2022-03-31 21:39:26 -05:00

161 lines
5.7 KiB
C#

using Content.Shared.Atmos;
using Robust.Shared.Map;
namespace Content.Server.Explosion.EntitySystems;
public sealed class SpaceExplosion : TileExplosion
{
/// <summary>
/// The keys of this dictionary correspond to space tiles that intersect a grid. The values have information
/// about what grid (which could be more than one), and in what directions the space-based explosion is allowed
/// to propagate from this tile.
/// </summary>
private Dictionary<Vector2i, BlockedSpaceTile> _gridBlockMap;
/// <summary>
/// After every iteration, this data set will store all the grid-tiles that were reached as a result of the
/// explosion expanding in space.
/// </summary>
public Dictionary<GridId, HashSet<Vector2i>> GridJump = new();
public ushort TileSize = ExplosionSystem.DefaultTileSize;
public SpaceExplosion(ExplosionSystem system, MapCoordinates epicentre, GridId? referenceGrid, List<GridId> localGrids, float maxDistance)
{
(_gridBlockMap, TileSize) = system.TransformGridEdges(epicentre, referenceGrid, localGrids, maxDistance);
system.GetUnblockedDirections(_gridBlockMap, TileSize);
}
public int AddNewTiles(int iteration, HashSet<Vector2i> inputSpaceTiles)
{
NewTiles = new();
NewBlockedTiles = new();
NewFreedTiles = new();
GridJump = new();
// Adjacent tiles
if (TileLists.TryGetValue(iteration - 2, out var adjacent))
AddNewAdjacentTiles(iteration, adjacent);
if (FreedTileLists.TryGetValue((iteration - 2) % 3, out var delayedAdjacent))
AddNewAdjacentTiles(iteration, delayedAdjacent);
// Diagonal tiles
if (TileLists.TryGetValue(iteration - 3, out var diagonal))
AddNewDiagonalTiles(iteration, diagonal);
if (FreedTileLists.TryGetValue((iteration - 3) % 3, out var delayedDiagonal))
AddNewDiagonalTiles(iteration, delayedDiagonal);
// Tiles entering space from some grid.
foreach (var tile in inputSpaceTiles)
{
ProcessNewTile(iteration, tile, AtmosDirection.All);
}
// Store new tiles
if (NewTiles.Count != 0)
TileLists[iteration] = NewTiles;
if (NewBlockedTiles.Count != 0)
BlockedTileLists[iteration] = NewBlockedTiles;
FreedTileLists[iteration % 3] = NewFreedTiles;
// return new tile count
return NewTiles.Count + NewBlockedTiles.Count;
}
private void JumpToGrid(BlockedSpaceTile blocker)
{
foreach (var edge in blocker.BlockingGridEdges)
{
if (edge.Grid == null) continue;
if (!GridJump.TryGetValue(edge.Grid.Value, out var set))
{
set = new();
GridJump[edge.Grid.Value] = set;
}
set.Add(edge.Tile);
}
}
private void AddNewAdjacentTiles(int iteration, IEnumerable<Vector2i> tiles)
{
foreach (var tile in tiles)
{
var unblockedDirections = GetUnblockedDirectionOrAll(tile);
if (unblockedDirections == AtmosDirection.Invalid)
continue;
for (var i = 0; i < Atmospherics.Directions; i++)
{
var direction = (AtmosDirection) (1 << i);
if (!unblockedDirections.IsFlagSet(direction))
continue; // explosion cannot propagate in this direction. Ever.
ProcessNewTile(iteration, tile.Offset(direction), direction.GetOpposite());
}
}
}
public override void InitTile(Vector2i initialTile)
{
ProcessedTiles.Add(initialTile);
TileLists[0] = new() { initialTile };
// It might be the case that the initial space-explosion tile actually overlaps on a grid. In that case we
// need to manually add it to the `spaceToGridTiles` dictionary. This would normally be done automatically
// during the neighbor finding steps.
if (_gridBlockMap.TryGetValue(initialTile, out var blocker))
JumpToGrid(blocker);
}
protected override void ProcessNewTile(int iteration, Vector2i tile, AtmosDirection entryDirection)
{
if (!_gridBlockMap.TryGetValue(tile, out var blocker))
{
// this tile does not intersect any grids. Add it (if its new) and continue.
if (ProcessedTiles.Add(tile))
NewTiles.Add(tile);
return;
}
// Is the entry to this tile blocked?
if ((blocker.UnblockedDirections & entryDirection) == 0)
{
// was this tile already entered from some other direction?
if (EnteredBlockedTiles.Contains(tile))
return;
// Did the explosion already attempt to enter this tile from some other direction?
if (!UnenteredBlockedTiles.Add(tile))
return;
// First time the explosion is reaching this tile.
NewBlockedTiles.Add(tile);
JumpToGrid(blocker);
}
// Was this tile already entered?
if (!EnteredBlockedTiles.Add(tile))
return;
// Did the explosion already attempt to enter this tile from some other direction?
if (UnenteredBlockedTiles.Contains(tile))
{
NewFreedTiles.Add(tile);
return;
}
// This is a completely new tile, and we just so happened to enter it from an unblocked direction.
NewTiles.Add(tile);
JumpToGrid(blocker);
}
protected override AtmosDirection GetUnblockedDirectionOrAll(Vector2i tile)
{
return _gridBlockMap.TryGetValue(tile, out var blocker) ? blocker.UnblockedDirections : AtmosDirection.All;
}
}