Explosion refactor fixes pt1 (#7375)

This commit is contained in:
Leon Friedrich
2022-04-05 19:22:35 +12:00
committed by GitHub
parent 722a408c41
commit 311450864c
15 changed files with 239 additions and 78 deletions

View File

@@ -1,6 +1,4 @@
using System.Linq;
using Content.Server.Explosion.Components;
using Content.Server.Throwing;
using Content.Shared.Damage;
using Content.Shared.Explosion;
using Content.Shared.Maps;
@@ -9,7 +7,6 @@ using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Server.Explosion.EntitySystems;
@@ -54,6 +51,19 @@ public sealed partial class ExplosionSystem : EntitySystem
/// </summary>
private int _previousTileIteration;
private void OnMapChanged(MapChangedEvent ev)
{
// If a map was deleted, check the explosion currently being processed belongs to that map.
if (ev.Created)
return;
if (_activeExplosion?.Epicenter.MapId != ev.Map)
return;
_activeExplosion = null;
_nodeGroupSystem.Snoozing = false;
}
/// <summary>
/// Process the explosion queue.
/// </summary>
@@ -287,8 +297,11 @@ public sealed partial class ExplosionSystem : EntitySystem
if (_containerSystem.IsEntityInContainer(uid, meta))
return;
// "worldPos" should be the space/map local position.
var worldPos = _transformSystem.GetWorldPosition(xform, xformQuery);
// finally check if it intersects our tile
if (gridBox.Contains(invSpaceMatrix.Transform(xform.LocalPosition)))
if (gridBox.Contains(invSpaceMatrix.Transform(worldPos)))
list.Add((uid, xform));
};
@@ -365,15 +378,18 @@ public sealed partial class ExplosionSystem : EntitySystem
/// grid tile.
/// </summary>
public void DamageFloorTile(TileRef tileRef,
float intensity,
float effectiveIntensity,
int maxTileBreak,
List<(Vector2i GridIndices, Tile Tile)> damagedTiles,
ExplosionPrototype type)
{
var tileDef = _tileDefinitionManager[tileRef.Tile.TypeId];
while (_robustRandom.Prob(type.TileBreakChance(intensity)))
int tileBreakages = 0;
while (maxTileBreak > tileBreakages && _robustRandom.Prob(type.TileBreakChance(effectiveIntensity)))
{
intensity -= type.TileBreakRerollReduction;
tileBreakages++;
effectiveIntensity -= type.TileBreakRerollReduction;
if (tileDef is not ContentTileDefinition contentTileDef)
break;
@@ -397,19 +413,41 @@ public sealed partial class ExplosionSystem : EntitySystem
/// cref="ExplosionSystem"/>.
/// </summary>
/// <remarks>
/// This is basically the output of <see cref="ExplosionSystem.GetExplosionTiles()"/>, but wrapped in an enumerator
/// to iterate over the tiles, along with the ability to keep track of what entities have already been damaged by
/// This is basically the output of <see cref="ExplosionSystem.GetExplosionTiles()"/>, but with some utility functions for
/// iterating over the tiles, along with the ability to keep track of what entities have already been damaged by
/// this explosion.
/// </remarks>
sealed class Explosion
{
/// <summary>
/// For every grid (+ space) that the explosion reached, this data struct stores information about the tiles and
/// caches the entity-lookup component so that it doesn't have to be re-fetched for every tile.
/// </summary>
struct ExplosionData
{
public EntityLookupComponent Lookup;
/// <summary>
/// The tiles that the explosion damaged, grouped by the iteration (can be thought of as the distance from the epicenter)
/// </summary>
public Dictionary<int, List<Vector2i>> TileLists;
/// <summary>
/// Lookup component for this grid (or space/map).
/// </summary>
public EntityLookupComponent Lookup;
/// <summary>
/// The actual grid that this corresponds to. If null, this implies space.
/// </summary>
public IMapGrid? MapGrid;
}
private readonly List<ExplosionData> _explosionData = new();
/// <summary>
/// The explosion intensity associated with each tile iteration.
/// </summary>
private readonly List<float> _tileSetIntensity;
/// <summary>
/// Used to avoid applying explosion effects repeatedly to the same entity. Particularly important if the
/// explosion throws this entity, as then it will be moving while the explosion is happening.
@@ -417,21 +455,36 @@ sealed class Explosion
public readonly HashSet<EntityUid> ProcessedEntities = new();
/// <summary>
/// This integer tracks how much of this explosion has been processed.
/// This integer tracks how much of this explosion has been processed.
/// </summary>
public int CurrentIteration { get; private set; } = 0;
/// <summary>
/// The prototype for this explosion. Determines tile break chance, damage, etc.
/// </summary>
public readonly ExplosionPrototype ExplosionType;
/// <summary>
/// The center of the explosion. Used for physics throwing. Also used to identify the map on which the explosion is happening.
/// </summary>
public readonly MapCoordinates Epicenter;
/// <summary>
/// The matrix that defines the referance frame for the explosion in space.
/// </summary>
private readonly Matrix3 _spaceMatrix;
/// <summary>
/// Inverse of <see cref="_spaceMatrix"/>
/// </summary>
private readonly Matrix3 _invSpaceMatrix;
private readonly List<ExplosionData> _explosionData = new();
private readonly List<float> _tileSetIntensity;
/// <summary>
/// Have all the tiles on all the grids been processed?
/// </summary>
public bool FinishedProcessing;
// shitty enumerator implementation
// Variables used for enumerating over tiles, grids, etc
private DamageSpecifier _currentDamage = default!;
private EntityLookupComponent _currentLookup = default!;
private IMapGrid? _currentGrid;
@@ -439,25 +492,50 @@ sealed class Explosion
private float _currentThrowForce;
private List<Vector2i>.Enumerator _currentEnumerator;
private int _currentDataIndex;
private Dictionary<IMapGrid, List<(Vector2i, Tile)>> _tileUpdateDict = new();
private EntityQuery<TransformComponent> _xformQuery;
private EntityQuery<PhysicsComponent> _physicsQuery;
private EntityQuery<DamageableComponent> _damageQuery;
private EntityQuery<MetaDataComponent> _metaQuery;
/// <summary>
/// The set of tiles that need to be updated when the explosion has finished processing. Used to avoid having
/// the explosion trigger chunk regeneration & shuttle-system processing every tick.
/// </summary>
private readonly Dictionary<IMapGrid, List<(Vector2i, Tile)>> _tileUpdateDict = new();
public int Area;
// Entity Queries
private readonly EntityQuery<TransformComponent> _xformQuery;
private readonly EntityQuery<PhysicsComponent> _physicsQuery;
private readonly EntityQuery<DamageableComponent> _damageQuery;
private readonly EntityQuery<MetaDataComponent> _metaQuery;
/// <summary>
/// Total area that the explosion covers.
/// </summary>
public readonly int Area;
/// <summary>
/// factor used to scale the tile break chances.
/// </summary>
private readonly float _tileBreakScale;
/// <summary>
/// Maximum number of times that an explosion will break a single tile.
/// </summary>
private readonly int _maxTileBreak;
private readonly IEntityManager _entMan;
private readonly ExplosionSystem _system;
/// <summary>
/// Initialize a new instance for processing
/// </summary>
public Explosion(ExplosionSystem system,
ExplosionPrototype explosionType,
SpaceExplosion? spaceData,
List<GridExplosion> gridData,
ExplosionSpaceTileFlood? spaceData,
List<ExplosionGridTileFlood> gridData,
List<float> tileSetIntensity,
MapCoordinates epicenter,
Matrix3 spaceMatrix,
int area,
float tileBreakScale,
int maxTileBreak,
IEntityManager entMan,
IMapManager mapMan)
{
@@ -467,6 +545,10 @@ sealed class Explosion
Epicenter = epicenter;
Area = area;
_tileBreakScale = tileBreakScale;
_maxTileBreak = maxTileBreak;
_entMan = entMan;
_xformQuery = entMan.GetEntityQuery<TransformComponent>();
_physicsQuery = entMan.GetEntityQuery<PhysicsComponent>();
_damageQuery = entMan.GetEntityQuery<DamageableComponent>();
@@ -501,6 +583,10 @@ sealed class Explosion
MoveNext();
}
/// <summary>
/// Find the next tile-enumerator. This either means retrieving a set of tiles on the next grid, or incrementing
/// the tile iteration by one and moving back to the first grid. This will also update the current damage, current entity-lookup, etc.
/// </summary>
private bool TryGetNextTileEnumerator()
{
while (CurrentIteration < _tileSetIntensity.Count)
@@ -526,21 +612,29 @@ sealed class Explosion
_currentEnumerator = tileList.GetEnumerator();
_currentLookup = _explosionData[_currentDataIndex].Lookup;
_currentGrid = _explosionData[_currentDataIndex].MapGrid;
_currentDataIndex++;
// sanity checks, in case something changed while the explosion was being processed over several ticks.
if (_currentLookup.Deleted || _currentGrid != null && !_entMan.EntityExists(_currentGrid.GridEntityId))
continue;
return true;
}
// this explosion intensity has been fully processed, move to the next one
// All the tiles belonging to this explosion iteration have been processed. Move onto the next iteration and
// reset the grid counter.
CurrentIteration++;
_currentDataIndex = 0;
}
// no more explosion data to process
// No more explosion tiles to process
FinishedProcessing = true;
return false;
}
/// <summary>
/// Get the next tile that needs processing
/// </summary>
private bool MoveNext()
{
if (FinishedProcessing)
@@ -557,6 +651,9 @@ sealed class Explosion
return false;
}
/// <summary>
/// Attempt to process (i.e., damage entities) some number of grid tiles.
/// </summary>
public int Process(int processingTarget)
{
// In case the explosion terminated early last tick due to exceeding the allocated processing time, use this
@@ -572,6 +669,7 @@ sealed class Explosion
break;
}
// Is the current tile on a grid (instead of in space)?
if (_currentGrid != null &&
_currentGrid.TryGetTileRef(_currentEnumerator.Current, out var tileRef) &&
!tileRef.Tile.IsEmpty)
@@ -582,6 +680,8 @@ sealed class Explosion
_tileUpdateDict[_currentGrid] = tileUpdateList;
}
// damage entities on the tile. Also figures out whether there are any solid entities blocking the floor
// from being destroyed.
var canDamageFloor = _system.ExplodeTile(_currentLookup,
_currentGrid,
_currentEnumerator.Current,
@@ -595,12 +695,13 @@ sealed class Explosion
_physicsQuery,
_metaQuery);
// was there a blocking entity on the tile that was not destroyed by the explosion?
// If the floor is not blocked by some dense object, damage the floor tiles.
if (canDamageFloor)
_system.DamageFloorTile(tileRef, _currentIntensity, tileUpdateList, ExplosionType);
_system.DamageFloorTile(tileRef, _currentIntensity * _tileBreakScale, _maxTileBreak, tileUpdateList, ExplosionType);
}
else
{
// The current "tile" is in space. Damage any entities in that region
_system.ExplodeSpace(_currentLookup,
_spaceMatrix,
_invSpaceMatrix,
@@ -620,12 +721,16 @@ sealed class Explosion
break;
}
// Update damaged/broken tiles on the grid.
SetTiles();
return processed;
}
private void SetTiles()
{
// Updating the grid can result in chunk collision regeneration & slow processing by the shuttle system.
// Therefore, tile breaking may be configure to only happen at the end of an explosion, rather than during every
// tick.
if (!_system.IncrementalTileBreaking && !FinishedProcessing)
return;