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

@@ -33,6 +33,7 @@ public sealed class ExplosionOverlaySystem : EntitySystem
SubscribeNetworkEvent<ExplosionEvent>(OnExplosion); SubscribeNetworkEvent<ExplosionEvent>(OnExplosion);
SubscribeNetworkEvent<ExplosionOverlayUpdateEvent>(HandleExplosionUpdate); SubscribeNetworkEvent<ExplosionOverlayUpdateEvent>(HandleExplosionUpdate);
SubscribeLocalEvent<MapChangedEvent>(OnMapChanged);
_cfg.OnValueChanged(CCVars.ExplosionPersistence, SetExplosionPersistence, true); _cfg.OnValueChanged(CCVars.ExplosionPersistence, SetExplosionPersistence, true);
@@ -42,6 +43,17 @@ public sealed class ExplosionOverlaySystem : EntitySystem
overlayManager.AddOverlay(_overlay); overlayManager.AddOverlay(_overlay);
} }
private void OnMapChanged(MapChangedEvent ev)
{
if (ev.Created)
return;
if (_overlay.ActiveExplosion?.Map == ev.Map)
_overlay.ActiveExplosion = null;
_overlay.CompletedExplosions.RemoveAll(exp => exp.Map == ev.Map);
}
private void SetExplosionPersistence(float value) => ExplosionPersistence = value; private void SetExplosionPersistence(float value) => ExplosionPersistence = value;
public override void FrameUpdate(float frameTime) public override void FrameUpdate(float frameTime)

View File

@@ -153,7 +153,7 @@ namespace Content.Server.Administration
{ {
var coords = Transform(args.Target).MapPosition; var coords = Transform(args.Target).MapPosition;
Timer.Spawn(_gameTiming.TickPeriod, Timer.Spawn(_gameTiming.TickPeriod,
() => _explosionSystem.QueueExplosion(coords, ExplosionSystem.DefaultExplosionPrototypeId, 30, 4, 8), () => _explosionSystem.QueueExplosion(coords, ExplosionSystem.DefaultExplosionPrototypeId, 4, 1, 2, maxTileBreak: 0), // it gibs, damage doesn't need to be high.
CancellationToken.None); CancellationToken.None);
if (TryComp(args.Target, out SharedBodyComponent? body)) if (TryComp(args.Target, out SharedBodyComponent? body))

View File

@@ -51,6 +51,22 @@ public sealed class ExplosiveComponent : Component
[DataField("totalIntensity")] [DataField("totalIntensity")]
public float TotalIntensity = 10; public float TotalIntensity = 10;
/// <summary>
/// Factor used to scale the explosion intensity when calculating tile break chances. Allows for stronger
/// explosives that don't space tiles, without having to create a new explosion-type prototype.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("tileBreakScale")]
public float TileBreakScale = 1f;
/// <summary>
/// Maximum number of times that an explosive can break a tile. Currently, for normal space ships breaking a
/// tile twice will result in a vacuum.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("maxTileBreak")]
public int MaxTileBreak = int.MaxValue;
/// <summary> /// <summary>
/// Avoid somehow double-triggering this explosion (e.g. by damaging this entity from its own explosion. /// Avoid somehow double-triggering this explosion (e.g. by damaging this entity from its own explosion.
/// </summary> /// </summary>

View File

@@ -4,7 +4,10 @@ using static Content.Server.Explosion.EntitySystems.ExplosionSystem;
namespace Content.Server.Explosion.EntitySystems; namespace Content.Server.Explosion.EntitySystems;
public sealed class GridExplosion : TileExplosion /// <summary>
/// See <see cref="ExplosionTileFlood"/>. Each instance of this class corresponds to a seperate grid.
/// </summary>
public sealed class ExplosionGridTileFlood : ExplosionTileFlood
{ {
public IMapGrid Grid; public IMapGrid Grid;
private bool _needToTransform = false; private bool _needToTransform = false;
@@ -31,7 +34,7 @@ public sealed class GridExplosion : TileExplosion
private Dictionary<Vector2i, NeighborFlag> _edgeTiles; private Dictionary<Vector2i, NeighborFlag> _edgeTiles;
public GridExplosion( public ExplosionGridTileFlood(
IMapGrid grid, IMapGrid grid,
Dictionary<Vector2i, TileData> airtightMap, Dictionary<Vector2i, TileData> airtightMap,
float maxIntensity, float maxIntensity,

View File

@@ -3,7 +3,10 @@ using Robust.Shared.Map;
namespace Content.Server.Explosion.EntitySystems; namespace Content.Server.Explosion.EntitySystems;
public sealed class SpaceExplosion : TileExplosion /// <summary>
/// See <see cref="ExplosionTileFlood"/>.
/// </summary>
public sealed class ExplosionSpaceTileFlood : ExplosionTileFlood
{ {
/// <summary> /// <summary>
/// The keys of this dictionary correspond to space tiles that intersect a grid. The values have information /// The keys of this dictionary correspond to space tiles that intersect a grid. The values have information
@@ -20,7 +23,7 @@ public sealed class SpaceExplosion : TileExplosion
public ushort TileSize = ExplosionSystem.DefaultTileSize; public ushort TileSize = ExplosionSystem.DefaultTileSize;
public SpaceExplosion(ExplosionSystem system, MapCoordinates epicentre, GridId? referenceGrid, List<GridId> localGrids, float maxDistance) public ExplosionSpaceTileFlood(ExplosionSystem system, MapCoordinates epicentre, GridId? referenceGrid, List<GridId> localGrids, float maxDistance)
{ {
(_gridBlockMap, TileSize) = system.TransformGridEdges(epicentre, referenceGrid, localGrids, maxDistance); (_gridBlockMap, TileSize) = system.TransformGridEdges(epicentre, referenceGrid, localGrids, maxDistance);
system.GetUnblockedDirections(_gridBlockMap, TileSize); system.GetUnblockedDirections(_gridBlockMap, TileSize);

View File

@@ -163,7 +163,6 @@ public sealed partial class ExplosionSystem : EntitySystem
return explosionTolerance; return explosionTolerance;
} }
}
/// <summary> /// <summary>
/// Data struct that describes the explosion-blocking airtight entities on a tile. /// Data struct that describes the explosion-blocking airtight entities on a tile.
@@ -179,3 +178,4 @@ public struct TileData
public float[] ExplosionTolerance; public float[] ExplosionTolerance;
public AtmosDirection BlockedDirections = AtmosDirection.Invalid; public AtmosDirection BlockedDirections = AtmosDirection.Invalid;
} }
}

View File

@@ -1,6 +1,4 @@
using System.Linq; using System.Linq;
using Content.Server.Explosion.Components;
using Content.Server.Throwing;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Explosion; using Content.Shared.Explosion;
using Content.Shared.Maps; using Content.Shared.Maps;
@@ -9,7 +7,6 @@ using Robust.Shared.Map;
using Robust.Shared.Physics; using Robust.Shared.Physics;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Server.Explosion.EntitySystems; namespace Content.Server.Explosion.EntitySystems;
@@ -54,6 +51,19 @@ public sealed partial class ExplosionSystem : EntitySystem
/// </summary> /// </summary>
private int _previousTileIteration; 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> /// <summary>
/// Process the explosion queue. /// Process the explosion queue.
/// </summary> /// </summary>
@@ -287,8 +297,11 @@ public sealed partial class ExplosionSystem : EntitySystem
if (_containerSystem.IsEntityInContainer(uid, meta)) if (_containerSystem.IsEntityInContainer(uid, meta))
return; return;
// "worldPos" should be the space/map local position.
var worldPos = _transformSystem.GetWorldPosition(xform, xformQuery);
// finally check if it intersects our tile // finally check if it intersects our tile
if (gridBox.Contains(invSpaceMatrix.Transform(xform.LocalPosition))) if (gridBox.Contains(invSpaceMatrix.Transform(worldPos)))
list.Add((uid, xform)); list.Add((uid, xform));
}; };
@@ -365,15 +378,18 @@ public sealed partial class ExplosionSystem : EntitySystem
/// grid tile. /// grid tile.
/// </summary> /// </summary>
public void DamageFloorTile(TileRef tileRef, public void DamageFloorTile(TileRef tileRef,
float intensity, float effectiveIntensity,
int maxTileBreak,
List<(Vector2i GridIndices, Tile Tile)> damagedTiles, List<(Vector2i GridIndices, Tile Tile)> damagedTiles,
ExplosionPrototype type) ExplosionPrototype type)
{ {
var tileDef = _tileDefinitionManager[tileRef.Tile.TypeId]; 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) if (tileDef is not ContentTileDefinition contentTileDef)
break; break;
@@ -397,19 +413,41 @@ public sealed partial class ExplosionSystem : EntitySystem
/// cref="ExplosionSystem"/>. /// cref="ExplosionSystem"/>.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This is basically the output of <see cref="ExplosionSystem.GetExplosionTiles()"/>, but wrapped in an enumerator /// This is basically the output of <see cref="ExplosionSystem.GetExplosionTiles()"/>, but with some utility functions for
/// to iterate over the tiles, along with the ability to keep track of what entities have already been damaged by /// iterating over the tiles, along with the ability to keep track of what entities have already been damaged by
/// this explosion. /// this explosion.
/// </remarks> /// </remarks>
sealed class Explosion 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 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; 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; 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> /// <summary>
/// Used to avoid applying explosion effects repeatedly to the same entity. Particularly important if the /// 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. /// explosion throws this entity, as then it will be moving while the explosion is happening.
@@ -421,17 +459,32 @@ sealed class Explosion
/// </summary> /// </summary>
public int CurrentIteration { get; private set; } = 0; public int CurrentIteration { get; private set; } = 0;
/// <summary>
/// The prototype for this explosion. Determines tile break chance, damage, etc.
/// </summary>
public readonly ExplosionPrototype ExplosionType; 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; public readonly MapCoordinates Epicenter;
/// <summary>
/// The matrix that defines the referance frame for the explosion in space.
/// </summary>
private readonly Matrix3 _spaceMatrix; private readonly Matrix3 _spaceMatrix;
/// <summary>
/// Inverse of <see cref="_spaceMatrix"/>
/// </summary>
private readonly Matrix3 _invSpaceMatrix; private readonly Matrix3 _invSpaceMatrix;
private readonly List<ExplosionData> _explosionData = new(); /// <summary>
private readonly List<float> _tileSetIntensity; /// Have all the tiles on all the grids been processed?
/// </summary>
public bool FinishedProcessing; public bool FinishedProcessing;
// shitty enumerator implementation // Variables used for enumerating over tiles, grids, etc
private DamageSpecifier _currentDamage = default!; private DamageSpecifier _currentDamage = default!;
private EntityLookupComponent _currentLookup = default!; private EntityLookupComponent _currentLookup = default!;
private IMapGrid? _currentGrid; private IMapGrid? _currentGrid;
@@ -439,25 +492,50 @@ sealed class Explosion
private float _currentThrowForce; private float _currentThrowForce;
private List<Vector2i>.Enumerator _currentEnumerator; private List<Vector2i>.Enumerator _currentEnumerator;
private int _currentDataIndex; private int _currentDataIndex;
private Dictionary<IMapGrid, List<(Vector2i, Tile)>> _tileUpdateDict = new();
private EntityQuery<TransformComponent> _xformQuery; /// <summary>
private EntityQuery<PhysicsComponent> _physicsQuery; /// The set of tiles that need to be updated when the explosion has finished processing. Used to avoid having
private EntityQuery<DamageableComponent> _damageQuery; /// the explosion trigger chunk regeneration & shuttle-system processing every tick.
private EntityQuery<MetaDataComponent> _metaQuery; /// </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; private readonly ExplosionSystem _system;
/// <summary>
/// Initialize a new instance for processing
/// </summary>
public Explosion(ExplosionSystem system, public Explosion(ExplosionSystem system,
ExplosionPrototype explosionType, ExplosionPrototype explosionType,
SpaceExplosion? spaceData, ExplosionSpaceTileFlood? spaceData,
List<GridExplosion> gridData, List<ExplosionGridTileFlood> gridData,
List<float> tileSetIntensity, List<float> tileSetIntensity,
MapCoordinates epicenter, MapCoordinates epicenter,
Matrix3 spaceMatrix, Matrix3 spaceMatrix,
int area, int area,
float tileBreakScale,
int maxTileBreak,
IEntityManager entMan, IEntityManager entMan,
IMapManager mapMan) IMapManager mapMan)
{ {
@@ -467,6 +545,10 @@ sealed class Explosion
Epicenter = epicenter; Epicenter = epicenter;
Area = area; Area = area;
_tileBreakScale = tileBreakScale;
_maxTileBreak = maxTileBreak;
_entMan = entMan;
_xformQuery = entMan.GetEntityQuery<TransformComponent>(); _xformQuery = entMan.GetEntityQuery<TransformComponent>();
_physicsQuery = entMan.GetEntityQuery<PhysicsComponent>(); _physicsQuery = entMan.GetEntityQuery<PhysicsComponent>();
_damageQuery = entMan.GetEntityQuery<DamageableComponent>(); _damageQuery = entMan.GetEntityQuery<DamageableComponent>();
@@ -501,6 +583,10 @@ sealed class Explosion
MoveNext(); 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() private bool TryGetNextTileEnumerator()
{ {
while (CurrentIteration < _tileSetIntensity.Count) while (CurrentIteration < _tileSetIntensity.Count)
@@ -526,21 +612,29 @@ sealed class Explosion
_currentEnumerator = tileList.GetEnumerator(); _currentEnumerator = tileList.GetEnumerator();
_currentLookup = _explosionData[_currentDataIndex].Lookup; _currentLookup = _explosionData[_currentDataIndex].Lookup;
_currentGrid = _explosionData[_currentDataIndex].MapGrid; _currentGrid = _explosionData[_currentDataIndex].MapGrid;
_currentDataIndex++; _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; 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++; CurrentIteration++;
_currentDataIndex = 0; _currentDataIndex = 0;
} }
// no more explosion data to process // No more explosion tiles to process
FinishedProcessing = true; FinishedProcessing = true;
return false; return false;
} }
/// <summary>
/// Get the next tile that needs processing
/// </summary>
private bool MoveNext() private bool MoveNext()
{ {
if (FinishedProcessing) if (FinishedProcessing)
@@ -557,6 +651,9 @@ sealed class Explosion
return false; return false;
} }
/// <summary>
/// Attempt to process (i.e., damage entities) some number of grid tiles.
/// </summary>
public int Process(int processingTarget) public int Process(int processingTarget)
{ {
// In case the explosion terminated early last tick due to exceeding the allocated processing time, use this // 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; break;
} }
// Is the current tile on a grid (instead of in space)?
if (_currentGrid != null && if (_currentGrid != null &&
_currentGrid.TryGetTileRef(_currentEnumerator.Current, out var tileRef) && _currentGrid.TryGetTileRef(_currentEnumerator.Current, out var tileRef) &&
!tileRef.Tile.IsEmpty) !tileRef.Tile.IsEmpty)
@@ -582,6 +680,8 @@ sealed class Explosion
_tileUpdateDict[_currentGrid] = tileUpdateList; _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, var canDamageFloor = _system.ExplodeTile(_currentLookup,
_currentGrid, _currentGrid,
_currentEnumerator.Current, _currentEnumerator.Current,
@@ -595,12 +695,13 @@ sealed class Explosion
_physicsQuery, _physicsQuery,
_metaQuery); _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) if (canDamageFloor)
_system.DamageFloorTile(tileRef, _currentIntensity, tileUpdateList, ExplosionType); _system.DamageFloorTile(tileRef, _currentIntensity * _tileBreakScale, _maxTileBreak, tileUpdateList, ExplosionType);
} }
else else
{ {
// The current "tile" is in space. Damage any entities in that region
_system.ExplodeSpace(_currentLookup, _system.ExplodeSpace(_currentLookup,
_spaceMatrix, _spaceMatrix,
_invSpaceMatrix, _invSpaceMatrix,
@@ -620,12 +721,16 @@ sealed class Explosion
break; break;
} }
// Update damaged/broken tiles on the grid.
SetTiles(); SetTiles();
return processed; return processed;
} }
private void SetTiles() 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) if (!_system.IncrementalTileBreaking && !FinishedProcessing)
return; return;

View File

@@ -22,7 +22,7 @@ public sealed partial class ExplosionSystem : EntitySystem
/// <param name="maxIntensity">The maximum intensity that the explosion can have at any given tile. This /// <param name="maxIntensity">The maximum intensity that the explosion can have at any given tile. This
/// effectively caps the damage that this explosion can do.</param> /// effectively caps the damage that this explosion can do.</param>
/// <returns>A list of tile-sets and a list of intensity values which describe the explosion.</returns> /// <returns>A list of tile-sets and a list of intensity values which describe the explosion.</returns>
private (int, List<float>, SpaceExplosion?, Dictionary<GridId, GridExplosion>, Matrix3)? GetExplosionTiles( private (int, List<float>, ExplosionSpaceTileFlood?, Dictionary<GridId, ExplosionGridTileFlood>, Matrix3)? GetExplosionTiles(
MapCoordinates epicenter, MapCoordinates epicenter,
string typeID, string typeID,
float totalIntensity, float totalIntensity,
@@ -64,8 +64,8 @@ public sealed partial class ExplosionSystem : EntitySystem
} }
// Main data for the exploding tiles in space and on various grids // Main data for the exploding tiles in space and on various grids
Dictionary<GridId, GridExplosion> gridData = new(); Dictionary<GridId, ExplosionGridTileFlood> gridData = new();
SpaceExplosion? spaceData = null; ExplosionSpaceTileFlood? spaceData = null;
// The intensity slope is how much the intensity drop over a one-tile distance. The actual algorithm step-size is half of thhat. // The intensity slope is how much the intensity drop over a one-tile distance. The actual algorithm step-size is half of thhat.
var stepSize = slope / 2; var stepSize = slope / 2;
@@ -98,7 +98,7 @@ public sealed partial class ExplosionSystem : EntitySystem
if (!_airtightMap.TryGetValue(epicentreGrid.Value, out var airtightMap)) if (!_airtightMap.TryGetValue(epicentreGrid.Value, out var airtightMap))
airtightMap = new(); airtightMap = new();
var initialGridData = new GridExplosion( var initialGridData = new ExplosionGridTileFlood(
_mapManager.GetGrid(epicentreGrid.Value), _mapManager.GetGrid(epicentreGrid.Value),
airtightMap, airtightMap,
maxIntensity, maxIntensity,
@@ -116,7 +116,7 @@ public sealed partial class ExplosionSystem : EntitySystem
else else
{ {
// set up the space explosion data // set up the space explosion data
spaceData = new SpaceExplosion(this, epicenter, referenceGrid, localGrids, maxDistance); spaceData = new ExplosionSpaceTileFlood(this, epicenter, referenceGrid, localGrids, maxDistance);
spaceData.InitTile(initialTile); spaceData.InitTile(initialTile);
} }
@@ -187,7 +187,7 @@ public sealed partial class ExplosionSystem : EntitySystem
if (!_airtightMap.TryGetValue(grid, out var airtightMap)) if (!_airtightMap.TryGetValue(grid, out var airtightMap))
airtightMap = new(); airtightMap = new();
data = new GridExplosion( data = new ExplosionGridTileFlood(
_mapManager.GetGrid(grid), _mapManager.GetGrid(grid),
airtightMap, airtightMap,
maxIntensity, maxIntensity,
@@ -208,7 +208,7 @@ public sealed partial class ExplosionSystem : EntitySystem
// if space-data is null, but some grid-based explosion reached space, we need to initialize it. // if space-data is null, but some grid-based explosion reached space, we need to initialize it.
if (spaceData == null && previousSpaceJump.Count != 0) if (spaceData == null && previousSpaceJump.Count != 0)
spaceData = new SpaceExplosion(this, epicenter, referenceGrid, localGrids, maxDistance); spaceData = new ExplosionSpaceTileFlood(this, epicenter, referenceGrid, localGrids, maxDistance);
// If the explosion has reached space, do that neighbors finding step as well. // If the explosion has reached space, do that neighbors finding step as well.
if (spaceData != null) if (spaceData != null)

View File

@@ -16,7 +16,6 @@ using Robust.Shared.Map;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Server.Explosion.EntitySystems; namespace Content.Server.Explosion.EntitySystems;
@@ -29,7 +28,6 @@ public sealed partial class ExplosionSystem : EntitySystem
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly ContainerSystem _containerSystem = default!; [Dependency] private readonly ContainerSystem _containerSystem = default!;
@@ -38,6 +36,7 @@ public sealed partial class ExplosionSystem : EntitySystem
[Dependency] private readonly EntityLookupSystem _entityLookup = default!; [Dependency] private readonly EntityLookupSystem _entityLookup = default!;
[Dependency] private readonly AdminLogSystem _logsSystem = default!; [Dependency] private readonly AdminLogSystem _logsSystem = default!;
[Dependency] private readonly ThrowingSystem _throwingSystem = default!; [Dependency] private readonly ThrowingSystem _throwingSystem = default!;
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
/// <summary> /// <summary>
/// "Tile-size" for space when there are no nearby grids to use as a reference. /// "Tile-size" for space when there are no nearby grids to use as a reference.
@@ -62,12 +61,15 @@ public sealed partial class ExplosionSystem : EntitySystem
DebugTools.Assert(_prototypeManager.HasIndex<ExplosionPrototype>(DefaultExplosionPrototypeId)); DebugTools.Assert(_prototypeManager.HasIndex<ExplosionPrototype>(DefaultExplosionPrototypeId));
// handled in ExplosionSystemGridMap.cs // handled in ExplosionSystem.GridMap.cs
SubscribeLocalEvent<GridRemovalEvent>(OnGridRemoved); SubscribeLocalEvent<GridRemovalEvent>(OnGridRemoved);
SubscribeLocalEvent<GridStartupEvent>(OnGridStartup); SubscribeLocalEvent<GridStartupEvent>(OnGridStartup);
SubscribeLocalEvent<ExplosionResistanceComponent, GetExplosionResistanceEvent>(OnGetResistance); SubscribeLocalEvent<ExplosionResistanceComponent, GetExplosionResistanceEvent>(OnGetResistance);
SubscribeLocalEvent<TileChangedEvent>(OnTileChanged); SubscribeLocalEvent<TileChangedEvent>(OnTileChanged);
// Handled by ExplosionSystem.Processing.cs
SubscribeLocalEvent<MapChangedEvent>(OnMapChanged);
// handled in ExplosionSystemAirtight.cs // handled in ExplosionSystemAirtight.cs
SubscribeLocalEvent<AirtightComponent, DamageChangedEvent>(OnAirtightDamaged); SubscribeLocalEvent<AirtightComponent, DamageChangedEvent>(OnAirtightDamaged);
SubscribeCvars(); SubscribeCvars();
@@ -118,6 +120,8 @@ public sealed partial class ExplosionSystem : EntitySystem
(float) totalIntensity, (float) totalIntensity,
explosive.IntensitySlope, explosive.IntensitySlope,
explosive.MaxIntensity, explosive.MaxIntensity,
explosive.TileBreakScale,
explosive.MaxTileBreak,
user); user);
if (delete) if (delete)
@@ -184,13 +188,15 @@ public sealed partial class ExplosionSystem : EntitySystem
float totalIntensity, float totalIntensity,
float slope, float slope,
float maxTileIntensity, float maxTileIntensity,
float tileBreakScale = 1f,
int maxTileBreak = int.MaxValue,
EntityUid? user = null, EntityUid? user = null,
bool addLog = false) bool addLog = false)
{ {
var pos = Transform(uid).MapPosition; var pos = Transform(uid).MapPosition;
QueueExplosion(pos, typeId, totalIntensity, slope, maxTileIntensity, addLog: false); QueueExplosion(pos, typeId, totalIntensity, slope, maxTileIntensity, tileBreakScale, maxTileBreak, addLog: false);
if (!addLog) if (!addLog)
return; return;
@@ -211,6 +217,8 @@ public sealed partial class ExplosionSystem : EntitySystem
float totalIntensity, float totalIntensity,
float slope, float slope,
float maxTileIntensity, float maxTileIntensity,
float tileBreakScale = 1f,
int maxTileBreak = int.MaxValue,
bool addLog = false) bool addLog = false)
{ {
if (totalIntensity <= 0 || slope <= 0) if (totalIntensity <= 0 || slope <= 0)
@@ -226,7 +234,7 @@ public sealed partial class ExplosionSystem : EntitySystem
_logsSystem.Add(LogType.Explosion, LogImpact.High, $"Explosion spawned at {epicenter:coordinates} with intensity {totalIntensity} slope {slope}"); _logsSystem.Add(LogType.Explosion, LogImpact.High, $"Explosion spawned at {epicenter:coordinates} with intensity {totalIntensity} slope {slope}");
_explosionQueue.Enqueue(() => SpawnExplosion(epicenter, type, totalIntensity, _explosionQueue.Enqueue(() => SpawnExplosion(epicenter, type, totalIntensity,
slope, maxTileIntensity)); slope, maxTileIntensity, tileBreakScale, maxTileBreak));
} }
/// <summary> /// <summary>
@@ -238,8 +246,13 @@ public sealed partial class ExplosionSystem : EntitySystem
ExplosionPrototype type, ExplosionPrototype type,
float totalIntensity, float totalIntensity,
float slope, float slope,
float maxTileIntensity) float maxTileIntensity,
float tileBreakScale,
int maxTileBreak)
{ {
if (!_mapManager.MapExists(epicenter.MapId))
return null;
var results = GetExplosionTiles(epicenter, type.ID, totalIntensity, slope, maxTileIntensity); var results = GetExplosionTiles(epicenter, type.ID, totalIntensity, slope, maxTileIntensity);
if (results == null) if (results == null)
@@ -268,6 +281,8 @@ public sealed partial class ExplosionSystem : EntitySystem
epicenter, epicenter,
spaceMatrix, spaceMatrix,
area, area,
tileBreakScale,
maxTileBreak,
EntityManager, EntityManager,
_mapManager); _mapManager);
} }
@@ -275,7 +290,7 @@ public sealed partial class ExplosionSystem : EntitySystem
/// <summary> /// <summary>
/// Constructor for the shared <see cref="ExplosionEvent"/> using the server-exclusive explosion classes. /// Constructor for the shared <see cref="ExplosionEvent"/> using the server-exclusive explosion classes.
/// </summary> /// </summary>
internal ExplosionEvent GetExplosionEvent(MapCoordinates epicenter, string id, Matrix3 spaceMatrix, SpaceExplosion? spaceData, IEnumerable<GridExplosion> gridData, List<float> iterationIntensity) internal ExplosionEvent GetExplosionEvent(MapCoordinates epicenter, string id, Matrix3 spaceMatrix, ExplosionSpaceTileFlood? spaceData, IEnumerable<ExplosionGridTileFlood> gridData, List<float> iterationIntensity)
{ {
var spaceTiles = spaceData?.TileLists; var spaceTiles = spaceData?.TileLists;

View File

@@ -4,9 +4,16 @@ using System.Runtime.CompilerServices;
namespace Content.Server.Explosion.EntitySystems; namespace Content.Server.Explosion.EntitySystems;
/// <summary> /// <summary>
/// This is the base class for <see cref="SpaceExplosion"/> and <see cref="GridExplosion"/>. It just exists to avoid some code duplication, because those classes are generally quite distinct. /// This class exists to facilitate the iterative neighbor-finding / flooding algorithm used by explosions in <see
/// cref="ExplosionSystem.GetExplosionTiles"/>. This is the base class for <see cref="ExplosionSpaceTileFlood"/> and
/// <see cref="ExplosionGridTileFlood"/>, each of which contains additional code fro logic specific to grids or space.
/// </summary> /// </summary>
public abstract class TileExplosion /// <remarks>
/// The class stores information about the tiles that the explosion has currently reached, and provides functions to
/// perform a neighbor-finding iteration to expand the explosion area. It also has some functionality that allows
/// tiles to move between grids/space.
/// </remarks>
public abstract class ExplosionTileFlood
{ {
// Main tile data sets, mapping iterations onto tile lists // Main tile data sets, mapping iterations onto tile lists
public Dictionary<int, List<Vector2i>> TileLists = new(); public Dictionary<int, List<Vector2i>> TileLists = new();

View File

@@ -1004,10 +1004,9 @@
beepInterval: 1 beepInterval: 1
- type: Explosive - type: Explosive
explosionType: Default explosionType: Default
devastationRange: 1 maxIntensity: 15
heavyImpactRange: 2 intensitySlope: 5
lightImpactRange: 3 totalIntensity: 120
flashRange: 6
- type: ExplodeOnTrigger - type: ExplodeOnTrigger
- type: entity - type: entity

View File

@@ -30,9 +30,10 @@
state: payload-explosive-armed state: payload-explosive-armed
- type: Explosive - type: Explosive
explosionType: Default explosionType: Default
devastationRange: 0 # same as the standard grenade, but those numbers were also just picked out of a hat.
heavyImpactRange: 2 maxIntensity: 10
lightImpactRange: 4 intensitySlope: 3
totalIntensity: 120 # about a ~4 tile radius
flashRange: 7 flashRange: 7
- type: ExplodeOnTrigger - type: ExplodeOnTrigger
- type: Destructible - type: Destructible

View File

@@ -191,6 +191,7 @@
maxIntensity: 40 maxIntensity: 40
intensitySlope: 6 intensitySlope: 6
totalIntensity: 200 totalIntensity: 200
maxTileBreak: 1
- type: PointLight - type: PointLight
radius: 3.5 radius: 3.5
color: orange color: orange
@@ -209,10 +210,10 @@
- type: ExplodeOnTrigger - type: ExplodeOnTrigger
- type: Explosive - type: Explosive
explosionType: Default explosionType: Default
devastationRange: 0 maxIntensity: 2 # max 30 per tile
heavyImpactRange: 0 intensitySlope: 1
lightImpactRange: 1 totalIntensity: 4 # 60 total damage to distribute over tiles
flashRange: 10 maxTileBreak: 1
- type: PointLight - type: PointLight
radius: 3.5 radius: 3.5
color: orange color: orange

View File

@@ -96,10 +96,9 @@
- type: Explosive - type: Explosive
explosionType: Default explosionType: Default
# About a 3-3.5 tile radius, strong enough to break reinforced walls near centre. # About a 3-3.5 tile radius, strong enough to break reinforced walls near centre.
# Currently a big spacing hazzard.
totalIntensity: 800 totalIntensity: 800
intensitySlope: 15 intensitySlope: 15
maxIntensity: 45 maxTileBreak: 1 # for destroying walls, not spacing the hallway
- type: ExplodeOnTrigger - type: ExplodeOnTrigger
- type: Damageable - type: Damageable
damageContainer: Inorganic damageContainer: Inorganic

View File

@@ -63,10 +63,10 @@
- !type:ExplodeBehavior - !type:ExplodeBehavior
- type: Explosive - type: Explosive
explosionType: Default explosionType: Default
devastationRange: 1 # Same as AME, but numbers still picked from a hat.
heavyImpactRange: 3 maxIntensity: 100
lightImpactRange: 5 intensitySlope: 2
flashRange: 6 totalIntensity: 200
# Base Wallmount Generator # Base Wallmount Generator