Explosion refactor fixes pt1 (#7375)
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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,
|
||||||
@@ -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);
|
||||||
@@ -163,13 +163,12 @@ 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct TileData
|
public struct TileData
|
||||||
{
|
{
|
||||||
public TileData(float[] explosionTolerance, AtmosDirection blockedDirections)
|
public TileData(float[] explosionTolerance, AtmosDirection blockedDirections)
|
||||||
{
|
{
|
||||||
ExplosionTolerance = explosionTolerance;
|
ExplosionTolerance = explosionTolerance;
|
||||||
@@ -178,4 +177,5 @@ public struct TileData
|
|||||||
|
|
||||||
public float[] ExplosionTolerance;
|
public float[] ExplosionTolerance;
|
||||||
public AtmosDirection BlockedDirections = AtmosDirection.Invalid;
|
public AtmosDirection BlockedDirections = AtmosDirection.Invalid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user