diff --git a/Content.Client/Explosion/ExplosionOverlaySystem.cs b/Content.Client/Explosion/ExplosionOverlaySystem.cs index ba3ffb8bd4..c838d35663 100644 --- a/Content.Client/Explosion/ExplosionOverlaySystem.cs +++ b/Content.Client/Explosion/ExplosionOverlaySystem.cs @@ -33,6 +33,7 @@ public sealed class ExplosionOverlaySystem : EntitySystem SubscribeNetworkEvent(OnExplosion); SubscribeNetworkEvent(HandleExplosionUpdate); + SubscribeLocalEvent(OnMapChanged); _cfg.OnValueChanged(CCVars.ExplosionPersistence, SetExplosionPersistence, true); @@ -42,6 +43,17 @@ public sealed class ExplosionOverlaySystem : EntitySystem 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; public override void FrameUpdate(float frameTime) diff --git a/Content.Server/Administration/AdminVerbSystem.cs b/Content.Server/Administration/AdminVerbSystem.cs index 11a58f0a56..3a286b3857 100644 --- a/Content.Server/Administration/AdminVerbSystem.cs +++ b/Content.Server/Administration/AdminVerbSystem.cs @@ -153,7 +153,7 @@ namespace Content.Server.Administration { var coords = Transform(args.Target).MapPosition; 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); if (TryComp(args.Target, out SharedBodyComponent? body)) diff --git a/Content.Server/Explosion/Components/ExplosiveComponent.cs b/Content.Server/Explosion/Components/ExplosiveComponent.cs index c116263c8e..b9fb4c4eac 100644 --- a/Content.Server/Explosion/Components/ExplosiveComponent.cs +++ b/Content.Server/Explosion/Components/ExplosiveComponent.cs @@ -51,6 +51,22 @@ public sealed class ExplosiveComponent : Component [DataField("totalIntensity")] public float TotalIntensity = 10; + /// + /// 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. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("tileBreakScale")] + public float TileBreakScale = 1f; + + /// + /// 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. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("maxTileBreak")] + public int MaxTileBreak = int.MaxValue; + /// /// Avoid somehow double-triggering this explosion (e.g. by damaging this entity from its own explosion. /// diff --git a/Content.Server/Explosion/EntitySystems/GridExplosion.cs b/Content.Server/Explosion/EntitySystems/ExplosionGridTileFlood.cs similarity index 97% rename from Content.Server/Explosion/EntitySystems/GridExplosion.cs rename to Content.Server/Explosion/EntitySystems/ExplosionGridTileFlood.cs index e1632d0c70..65e361438a 100644 --- a/Content.Server/Explosion/EntitySystems/GridExplosion.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionGridTileFlood.cs @@ -4,7 +4,10 @@ using static Content.Server.Explosion.EntitySystems.ExplosionSystem; namespace Content.Server.Explosion.EntitySystems; -public sealed class GridExplosion : TileExplosion +/// +/// See . Each instance of this class corresponds to a seperate grid. +/// +public sealed class ExplosionGridTileFlood : ExplosionTileFlood { public IMapGrid Grid; private bool _needToTransform = false; @@ -31,7 +34,7 @@ public sealed class GridExplosion : TileExplosion private Dictionary _edgeTiles; - public GridExplosion( + public ExplosionGridTileFlood( IMapGrid grid, Dictionary airtightMap, float maxIntensity, diff --git a/Content.Server/Explosion/EntitySystems/SpaceExplosion.cs b/Content.Server/Explosion/EntitySystems/ExplosionSpaceTileFlood.cs similarity index 95% rename from Content.Server/Explosion/EntitySystems/SpaceExplosion.cs rename to Content.Server/Explosion/EntitySystems/ExplosionSpaceTileFlood.cs index e00653ed88..241455bcaa 100644 --- a/Content.Server/Explosion/EntitySystems/SpaceExplosion.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSpaceTileFlood.cs @@ -3,7 +3,10 @@ using Robust.Shared.Map; namespace Content.Server.Explosion.EntitySystems; -public sealed class SpaceExplosion : TileExplosion +/// +/// See . +/// +public sealed class ExplosionSpaceTileFlood : ExplosionTileFlood { /// /// 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 SpaceExplosion(ExplosionSystem system, MapCoordinates epicentre, GridId? referenceGrid, List localGrids, float maxDistance) + public ExplosionSpaceTileFlood(ExplosionSystem system, MapCoordinates epicentre, GridId? referenceGrid, List localGrids, float maxDistance) { (_gridBlockMap, TileSize) = system.TransformGridEdges(epicentre, referenceGrid, localGrids, maxDistance); system.GetUnblockedDirections(_gridBlockMap, TileSize); diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs index 8da5486b38..30a3e391ad 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs @@ -163,19 +163,19 @@ public sealed partial class ExplosionSystem : EntitySystem return explosionTolerance; } -} -/// -/// Data struct that describes the explosion-blocking airtight entities on a tile. -/// -public struct TileData -{ - public TileData(float[] explosionTolerance, AtmosDirection blockedDirections) + /// + /// Data struct that describes the explosion-blocking airtight entities on a tile. + /// + public struct TileData { - ExplosionTolerance = explosionTolerance; - BlockedDirections = blockedDirections; - } + public TileData(float[] explosionTolerance, AtmosDirection blockedDirections) + { + ExplosionTolerance = explosionTolerance; + BlockedDirections = blockedDirections; + } - public float[] ExplosionTolerance; - public AtmosDirection BlockedDirections = AtmosDirection.Invalid; + public float[] ExplosionTolerance; + public AtmosDirection BlockedDirections = AtmosDirection.Invalid; + } } diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs index e214203363..352e579d4d 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs @@ -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 /// 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; + } + /// /// Process the explosion queue. /// @@ -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. /// 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"/>. /// /// -/// This is basically the output of , 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 , 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. /// sealed class Explosion { + /// + /// 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. + /// struct ExplosionData { - public EntityLookupComponent Lookup; + /// + /// The tiles that the explosion damaged, grouped by the iteration (can be thought of as the distance from the epicenter) + /// public Dictionary> TileLists; + + /// + /// Lookup component for this grid (or space/map). + /// + public EntityLookupComponent Lookup; + + /// + /// The actual grid that this corresponds to. If null, this implies space. + /// public IMapGrid? MapGrid; } + private readonly List _explosionData = new(); + + /// + /// The explosion intensity associated with each tile iteration. + /// + private readonly List _tileSetIntensity; + /// /// 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 ProcessedEntities = new(); /// - /// This integer tracks how much of this explosion has been processed. + /// This integer tracks how much of this explosion has been processed. /// public int CurrentIteration { get; private set; } = 0; + /// + /// The prototype for this explosion. Determines tile break chance, damage, etc. + /// public readonly ExplosionPrototype ExplosionType; + + /// + /// The center of the explosion. Used for physics throwing. Also used to identify the map on which the explosion is happening. + /// public readonly MapCoordinates Epicenter; + + /// + /// The matrix that defines the referance frame for the explosion in space. + /// private readonly Matrix3 _spaceMatrix; + + /// + /// Inverse of + /// private readonly Matrix3 _invSpaceMatrix; - private readonly List _explosionData = new(); - private readonly List _tileSetIntensity; - + /// + /// Have all the tiles on all the grids been processed? + /// 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.Enumerator _currentEnumerator; private int _currentDataIndex; - private Dictionary> _tileUpdateDict = new(); - private EntityQuery _xformQuery; - private EntityQuery _physicsQuery; - private EntityQuery _damageQuery; - private EntityQuery _metaQuery; + /// + /// 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. + /// + private readonly Dictionary> _tileUpdateDict = new(); - public int Area; + // Entity Queries + private readonly EntityQuery _xformQuery; + private readonly EntityQuery _physicsQuery; + private readonly EntityQuery _damageQuery; + private readonly EntityQuery _metaQuery; + /// + /// Total area that the explosion covers. + /// + public readonly int Area; + + /// + /// factor used to scale the tile break chances. + /// + private readonly float _tileBreakScale; + + /// + /// Maximum number of times that an explosion will break a single tile. + /// + private readonly int _maxTileBreak; + + private readonly IEntityManager _entMan; private readonly ExplosionSystem _system; + /// + /// Initialize a new instance for processing + /// public Explosion(ExplosionSystem system, ExplosionPrototype explosionType, - SpaceExplosion? spaceData, - List gridData, + ExplosionSpaceTileFlood? spaceData, + List gridData, List 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(); _physicsQuery = entMan.GetEntityQuery(); _damageQuery = entMan.GetEntityQuery(); @@ -501,6 +583,10 @@ sealed class Explosion MoveNext(); } + /// + /// 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. + /// 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; } + /// + /// Get the next tile that needs processing + /// private bool MoveNext() { if (FinishedProcessing) @@ -557,6 +651,9 @@ sealed class Explosion return false; } + /// + /// Attempt to process (i.e., damage entities) some number of grid tiles. + /// 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; diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs index 4694fae048..b82b0e20b0 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs @@ -22,7 +22,7 @@ public sealed partial class ExplosionSystem : EntitySystem /// The maximum intensity that the explosion can have at any given tile. This /// effectively caps the damage that this explosion can do. /// A list of tile-sets and a list of intensity values which describe the explosion. - private (int, List, SpaceExplosion?, Dictionary, Matrix3)? GetExplosionTiles( + private (int, List, ExplosionSpaceTileFlood?, Dictionary, Matrix3)? GetExplosionTiles( MapCoordinates epicenter, string typeID, float totalIntensity, @@ -64,8 +64,8 @@ public sealed partial class ExplosionSystem : EntitySystem } // Main data for the exploding tiles in space and on various grids - Dictionary gridData = new(); - SpaceExplosion? spaceData = null; + Dictionary gridData = new(); + 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. var stepSize = slope / 2; @@ -98,7 +98,7 @@ public sealed partial class ExplosionSystem : EntitySystem if (!_airtightMap.TryGetValue(epicentreGrid.Value, out var airtightMap)) airtightMap = new(); - var initialGridData = new GridExplosion( + var initialGridData = new ExplosionGridTileFlood( _mapManager.GetGrid(epicentreGrid.Value), airtightMap, maxIntensity, @@ -116,7 +116,7 @@ public sealed partial class ExplosionSystem : EntitySystem else { // 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); } @@ -187,7 +187,7 @@ public sealed partial class ExplosionSystem : EntitySystem if (!_airtightMap.TryGetValue(grid, out var airtightMap)) airtightMap = new(); - data = new GridExplosion( + data = new ExplosionGridTileFlood( _mapManager.GetGrid(grid), airtightMap, 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 (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 (spaceData != null) diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs index 5a6ac55990..a958d6684a 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs @@ -16,7 +16,6 @@ using Robust.Shared.Map; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Content.Server.Explosion.EntitySystems; @@ -29,7 +28,6 @@ public sealed partial class ExplosionSystem : EntitySystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly DamageableSystem _damageableSystem = 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 AdminLogSystem _logsSystem = default!; [Dependency] private readonly ThrowingSystem _throwingSystem = default!; + [Dependency] private readonly SharedTransformSystem _transformSystem = default!; /// /// "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(DefaultExplosionPrototypeId)); - // handled in ExplosionSystemGridMap.cs + // handled in ExplosionSystem.GridMap.cs SubscribeLocalEvent(OnGridRemoved); SubscribeLocalEvent(OnGridStartup); SubscribeLocalEvent(OnGetResistance); SubscribeLocalEvent(OnTileChanged); + // Handled by ExplosionSystem.Processing.cs + SubscribeLocalEvent(OnMapChanged); + // handled in ExplosionSystemAirtight.cs SubscribeLocalEvent(OnAirtightDamaged); SubscribeCvars(); @@ -118,6 +120,8 @@ public sealed partial class ExplosionSystem : EntitySystem (float) totalIntensity, explosive.IntensitySlope, explosive.MaxIntensity, + explosive.TileBreakScale, + explosive.MaxTileBreak, user); if (delete) @@ -184,13 +188,15 @@ public sealed partial class ExplosionSystem : EntitySystem float totalIntensity, float slope, float maxTileIntensity, + float tileBreakScale = 1f, + int maxTileBreak = int.MaxValue, EntityUid? user = null, bool addLog = false) { 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) return; @@ -211,6 +217,8 @@ public sealed partial class ExplosionSystem : EntitySystem float totalIntensity, float slope, float maxTileIntensity, + float tileBreakScale = 1f, + int maxTileBreak = int.MaxValue, bool addLog = false) { 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}"); _explosionQueue.Enqueue(() => SpawnExplosion(epicenter, type, totalIntensity, - slope, maxTileIntensity)); + slope, maxTileIntensity, tileBreakScale, maxTileBreak)); } /// @@ -238,8 +246,13 @@ public sealed partial class ExplosionSystem : EntitySystem ExplosionPrototype type, float totalIntensity, 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); if (results == null) @@ -268,6 +281,8 @@ public sealed partial class ExplosionSystem : EntitySystem epicenter, spaceMatrix, area, + tileBreakScale, + maxTileBreak, EntityManager, _mapManager); } @@ -275,7 +290,7 @@ public sealed partial class ExplosionSystem : EntitySystem /// /// Constructor for the shared using the server-exclusive explosion classes. /// - internal ExplosionEvent GetExplosionEvent(MapCoordinates epicenter, string id, Matrix3 spaceMatrix, SpaceExplosion? spaceData, IEnumerable gridData, List iterationIntensity) + internal ExplosionEvent GetExplosionEvent(MapCoordinates epicenter, string id, Matrix3 spaceMatrix, ExplosionSpaceTileFlood? spaceData, IEnumerable gridData, List iterationIntensity) { var spaceTiles = spaceData?.TileLists; diff --git a/Content.Server/Explosion/EntitySystems/TileExplosion.cs b/Content.Server/Explosion/EntitySystems/ExplosionTileFlood.cs similarity index 91% rename from Content.Server/Explosion/EntitySystems/TileExplosion.cs rename to Content.Server/Explosion/EntitySystems/ExplosionTileFlood.cs index c09c9255d4..75c5bbe1fb 100644 --- a/Content.Server/Explosion/EntitySystems/TileExplosion.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionTileFlood.cs @@ -4,9 +4,16 @@ using System.Runtime.CompilerServices; namespace Content.Server.Explosion.EntitySystems; /// -/// This is the base class for and . It just exists to avoid some code duplication, because those classes are generally quite distinct. +/// This class exists to facilitate the iterative neighbor-finding / flooding algorithm used by explosions in . This is the base class for and +/// , each of which contains additional code fro logic specific to grids or space. /// -public abstract class TileExplosion +/// +/// 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. +/// +public abstract class ExplosionTileFlood { // Main tile data sets, mapping iterations onto tile lists public Dictionary> TileLists = new(); diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index abc11279f5..69b2f92831 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -1004,10 +1004,9 @@ beepInterval: 1 - type: Explosive explosionType: Default - devastationRange: 1 - heavyImpactRange: 2 - lightImpactRange: 3 - flashRange: 6 + maxIntensity: 15 + intensitySlope: 5 + totalIntensity: 120 - type: ExplodeOnTrigger - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Devices/payload.yml b/Resources/Prototypes/Entities/Objects/Devices/payload.yml index e8c4f64068..331924e5a2 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/payload.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/payload.yml @@ -30,9 +30,10 @@ state: payload-explosive-armed - type: Explosive explosionType: Default - devastationRange: 0 - heavyImpactRange: 2 - lightImpactRange: 4 + # same as the standard grenade, but those numbers were also just picked out of a hat. + maxIntensity: 10 + intensitySlope: 3 + totalIntensity: 120 # about a ~4 tile radius flashRange: 7 - type: ExplodeOnTrigger - type: Destructible diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml index 75370e7b3f..19d0f7a630 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml @@ -191,6 +191,7 @@ maxIntensity: 40 intensitySlope: 6 totalIntensity: 200 + maxTileBreak: 1 - type: PointLight radius: 3.5 color: orange @@ -209,10 +210,10 @@ - type: ExplodeOnTrigger - type: Explosive explosionType: Default - devastationRange: 0 - heavyImpactRange: 0 - lightImpactRange: 1 - flashRange: 10 + maxIntensity: 2 # max 30 per tile + intensitySlope: 1 + totalIntensity: 4 # 60 total damage to distribute over tiles + maxTileBreak: 1 - type: PointLight radius: 3.5 color: orange diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml index cd5d02a37a..f0b810db00 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml @@ -96,10 +96,9 @@ - type: Explosive explosionType: Default # About a 3-3.5 tile radius, strong enough to break reinforced walls near centre. - # Currently a big spacing hazzard. totalIntensity: 800 intensitySlope: 15 - maxIntensity: 45 + maxTileBreak: 1 # for destroying walls, not spacing the hallway - type: ExplodeOnTrigger - type: Damageable damageContainer: Inorganic diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/generators.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/generators.yml index 52cf16849d..4898c75e3c 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/generators.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/generators.yml @@ -63,10 +63,10 @@ - !type:ExplodeBehavior - type: Explosive explosionType: Default - devastationRange: 1 - heavyImpactRange: 3 - lightImpactRange: 5 - flashRange: 6 + # Same as AME, but numbers still picked from a hat. + maxIntensity: 100 + intensitySlope: 2 + totalIntensity: 200 # Base Wallmount Generator