diff --git a/Content.Client/Explosion/ExplosionOverlay.cs b/Content.Client/Explosion/ExplosionOverlay.cs
index 7344164ff7..630efa9be2 100644
--- a/Content.Client/Explosion/ExplosionOverlay.cs
+++ b/Content.Client/Explosion/ExplosionOverlay.cs
@@ -1,3 +1,4 @@
+using Content.Shared.Explosion;
using JetBrains.Annotations;
using Robust.Client.Graphics;
using Robust.Shared.Enums;
@@ -10,26 +11,10 @@ namespace Content.Client.Explosion;
[UsedImplicitly]
public sealed class ExplosionOverlay : Overlay
{
- ///
- /// The explosion that needs to be drawn. This explosion is currently being processed by the server and
- /// expanding outwards.
- ///
- internal Explosion? ActiveExplosion;
-
- ///
- /// This index specifies what parts of the currently expanding explosion should be drawn.
- ///
- public int Index;
-
- ///
- /// These explosions have finished expanding, but we will draw for a few more frames. This is important for
- /// small explosions, as otherwise they disappear far too quickly.
- ///
- internal List CompletedExplosions = new ();
-
[Dependency] private readonly IRobustRandom _robustRandom = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IEntityManager _entMan = default!;
+ [Dependency] private readonly IPrototypeManager _proto = default!;
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
@@ -38,7 +23,7 @@ public sealed class ExplosionOverlay : Overlay
public ExplosionOverlay()
{
IoCManager.InjectDependencies(this);
- _shader = IoCManager.Resolve().Index("unshaded").Instance();
+ _shader = _proto.Index("unshaded").Instance();
}
protected override void Draw(in OverlayDrawArgs args)
@@ -48,22 +33,20 @@ public sealed class ExplosionOverlay : Overlay
var xforms = _entMan.GetEntityQuery();
- if (ActiveExplosion != null && ActiveExplosion.Map == args.Viewport.Eye?.Position.MapId)
+ foreach (var (comp, appearance) in _entMan.EntityQuery(true))
{
- DrawExplosion(drawHandle, args.WorldBounds, ActiveExplosion, Index, xforms);
- }
+ if (!appearance.TryGetData(ExplosionAppearanceData.Progress, out int index))
+ continue;
- foreach (var exp in CompletedExplosions)
- {
- if (exp.Map == args.Viewport.Eye?.Position.MapId)
- DrawExplosion(drawHandle, args.WorldBounds, exp, exp.Intensity.Count, xforms);
+ index = Math.Min(index, comp.Intensity.Count - 1);
+ DrawExplosion(drawHandle, args.WorldBounds, comp, index, xforms);
}
drawHandle.SetTransform(Matrix3.Identity);
drawHandle.UseShader(null);
}
- private void DrawExplosion(DrawingHandleWorld drawHandle, Box2Rotated worldBounds, Explosion exp, int index, EntityQuery xforms)
+ private void DrawExplosion(DrawingHandleWorld drawHandle, Box2Rotated worldBounds, ExplosionVisualsComponent exp, int index, EntityQuery xforms)
{
Box2 gridBounds;
foreach (var (gridId, tiles) in exp.Tiles)
@@ -94,10 +77,10 @@ public sealed class ExplosionOverlay : Overlay
Box2 gridBounds,
int index,
Dictionary> tileSets,
- Explosion exp,
+ ExplosionVisualsComponent exp,
ushort tileSize)
{
- for (var j = 0; j < index; j++)
+ for (var j = 0; j <= index; j++)
{
if (!tileSets.TryGetValue(j, out var tiles))
continue;
diff --git a/Content.Client/Explosion/ExplosionOverlaySystem.cs b/Content.Client/Explosion/ExplosionOverlaySystem.cs
index 81004167d3..6c7fafc463 100644
--- a/Content.Client/Explosion/ExplosionOverlaySystem.cs
+++ b/Content.Client/Explosion/ExplosionOverlaySystem.cs
@@ -1,11 +1,8 @@
-using Content.Shared.CCVar;
using Content.Shared.Explosion;
-using Content.Shared.GameTicking;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
-using Robust.Shared.Configuration;
-using Robust.Shared.Map;
+using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Client.Explosion;
@@ -16,12 +13,9 @@ namespace Content.Client.Explosion;
///
public sealed class ExplosionOverlaySystem : EntitySystem
{
- private ExplosionOverlay _overlay = default!;
-
[Dependency] private readonly IPrototypeManager _protoMan = default!;
- [Dependency] private readonly IMapManager _mapMan = default!;
[Dependency] private readonly IResourceCache _resCache = default!;
- [Dependency] private readonly IConfigurationManager _cfg = default!;
+ [Dependency] private readonly IOverlayManager _overlayMan = default!;
///
/// For how many seconds should an explosion stay on-screen once it has finished expanding?
@@ -32,209 +26,57 @@ public sealed class ExplosionOverlaySystem : EntitySystem
{
base.Initialize();
- SubscribeNetworkEvent(OnExplosion);
- SubscribeNetworkEvent(HandleExplosionUpdate);
- SubscribeLocalEvent(OnMapChanged);
- SubscribeAllEvent(OnReset);
-
- _cfg.OnValueChanged(CCVars.ExplosionPersistence, SetExplosionPersistence, true);
-
- var overlayManager = IoCManager.Resolve();
- _overlay = new ExplosionOverlay();
- if (!overlayManager.HasOverlay())
- overlayManager.AddOverlay(_overlay);
+ SubscribeLocalEvent(OnExplosionInit);
+ SubscribeLocalEvent(OnCompRemove);
+ SubscribeLocalEvent(OnExplosionHandleState);
+ _overlayMan.AddOverlay(new ExplosionOverlay());
}
- private void OnReset(RoundRestartCleanupEvent ev)
+ private void OnExplosionHandleState(EntityUid uid, ExplosionVisualsComponent component, ref ComponentHandleState args)
{
- // Not sure if round restart cleans up client-side entities, but better safe than sorry.
- foreach (var exp in _overlay.CompletedExplosions)
- {
- QueueDel(exp.LightEntity);
- }
- if (_overlay.ActiveExplosion != null)
- QueueDel(_overlay.ActiveExplosion.LightEntity);
-
- _overlay.CompletedExplosions.Clear();
- _overlay.ActiveExplosion = null;
- _overlay.Index = 0;
+ if (args.Current is not ExplosionVisualsState state)
+ return;
+
+ component.Epicenter = state.Epicenter;
+ component.SpaceTiles = state.SpaceTiles;
+ component.Tiles = state.Tiles;
+ component.Intensity = state.Intensity;
+ component.ExplosionType = state.ExplosionType;
+ component.SpaceMatrix = state.SpaceMatrix;
+ component.SpaceTileSize = state.SpaceTileSize;
}
- private void OnMapChanged(MapChangedEvent ev)
+ private void OnCompRemove(EntityUid uid, ExplosionVisualsComponent component, ComponentRemove args)
{
- if (ev.Created)
- return;
-
- if (_overlay.ActiveExplosion?.Map == ev.Map)
- _overlay.ActiveExplosion = null;
-
- _overlay.CompletedExplosions.RemoveAll(exp => exp.Map == ev.Map);
+ QueueDel(component.LightEntity);
}
- private void SetExplosionPersistence(float value) => ExplosionPersistence = value;
-
- public override void FrameUpdate(float frameTime)
+ private void OnExplosionInit(EntityUid uid, ExplosionVisualsComponent component, ComponentInit args)
{
- base.FrameUpdate(frameTime);
-
- // increment the lifetime of completed explosions, and remove them if they have been ons screen for more
- // than ExplosionPersistence seconds
- for (int i = _overlay.CompletedExplosions.Count - 1; i>= 0; i--)
- {
- var explosion = _overlay.CompletedExplosions[i];
-
- if (_mapMan.IsMapPaused(explosion.Map))
- continue;
-
- explosion.Lifetime += frameTime;
-
- if (explosion.Lifetime >= ExplosionPersistence)
- {
- EntityManager.QueueDeleteEntity(explosion.LightEntity);
-
- // Remove-swap
- _overlay.CompletedExplosions[i] = _overlay.CompletedExplosions[^1];
- _overlay.CompletedExplosions.RemoveAt(_overlay.CompletedExplosions.Count - 1);
- }
- }
- }
-
- ///
- /// The server has processed some explosion. This updates the client-side overlay so that the area covered
- /// by the fire-visual matches up with the area that the explosion has affected.
- ///
- private void HandleExplosionUpdate(ExplosionOverlayUpdateEvent args)
- {
- if (args.ExplosionId != _overlay.ActiveExplosion?.Explosionid && !IsNewer(args.ExplosionId))
- {
- // out of order events. Ignore.
- return;
- }
-
- _overlay.Index = args.Index;
-
- if (_overlay.ActiveExplosion == null)
- {
- // no active explosion... events out of order?
- return;
- }
-
- if (args.Index != int.MaxValue)
+ if (!_protoMan.TryIndex(component.ExplosionType, out ExplosionPrototype? type))
return;
- // the explosion has finished expanding
- _overlay.Index = 0;
- _overlay.CompletedExplosions.Add(_overlay.ActiveExplosion);
- _overlay.ActiveExplosion = null;
- }
-
- ///
- /// A new explosion occurred. This prepares the client-side light entity and stores the
- /// explosion/fire-effect overlay data.
- ///
- private void OnExplosion(ExplosionEvent args)
- {
- if (!_protoMan.TryIndex(args.TypeID, out ExplosionPrototype? type))
- return;
-
- // spawn in a light source at the epicenter
- var lightEntity = Spawn("ExplosionLight", args.Epicenter);
+ // spawn in a client-side light source at the epicenter
+ var lightEntity = Spawn("ExplosionLight", component.Epicenter);
var light = EnsureComp(lightEntity);
- light.Energy = light.Radius = args.Intensity.Count;
+ light.Energy = light.Radius = component.Intensity.Count;
light.Color = type.LightColor;
+ component.LightEntity = lightEntity;
+ component.FireColor = type.FireColor;
+ component.IntensityPerState = type.IntensityPerState;
- if (_overlay.ActiveExplosion == null)
+ var fireRsi = _resCache.GetResource(type.TexturePath).RSI;
+ foreach (var state in fireRsi)
{
- _overlay.ActiveExplosion = new(args, type, lightEntity, _resCache);
- return;
+ component.FireFrames.Add(state.GetFrames(RSI.State.Direction.South));
+ if (component.FireFrames.Count == type.FireStates)
+ break;
}
-
- // we have a currently active explosion. Can happen when events are received out of order. either multiple
- // explosions are happening in one tick, or a new explosion was received before the event telling us the old one
- // finished got through.
-
- if (IsNewer(args.ExplosionId))
- {
- // This is a newer explosion. Add the old-currently-active explosions to the completed list
- _overlay.CompletedExplosions.Add(_overlay.ActiveExplosion);
- _overlay.ActiveExplosion = new(args, type, lightEntity, _resCache);
- }
- else
- {
- // explosions were out of order. keep the active one, and directly add the received one to the completed
- // list.
- _overlay.CompletedExplosions.Add(new(args, type, lightEntity, _resCache));
- return;
- }
- }
-
- public bool IsNewer(int explosionId)
- {
- if (_overlay.ActiveExplosion == null)
- return true;
-
- // If we ever get servers stable enough to live this long, the explosion Id int might overflow.
- return _overlay.ActiveExplosion.Explosionid < explosionId
- || _overlay.ActiveExplosion.Explosionid > int.MaxValue/2 && explosionId < int.MinValue/2;
}
public override void Shutdown()
{
base.Shutdown();
-
- _cfg.UnsubValueChanged(CCVars.ExplosionPersistence, SetExplosionPersistence);
-
- var overlayManager = IoCManager.Resolve();
- if (overlayManager.HasOverlay())
- overlayManager.RemoveOverlay();
- }
-}
-
-internal sealed class Explosion
-{
- public readonly Dictionary>? SpaceTiles;
- public readonly Dictionary>> Tiles;
- public readonly List Intensity;
- public readonly EntityUid LightEntity;
- public readonly MapId Map;
- public readonly int Explosionid;
- public readonly ushort SpaceTileSize;
- public readonly float IntensityPerState;
-
- public readonly Matrix3 SpaceMatrix;
-
- ///
- /// How long have we been drawing this explosion, starting from the time the explosion was fully drawn.
- ///
- public float Lifetime;
-
- ///
- /// The textures used for the explosion fire effect. Each fire-state is associated with an explosion
- /// intensity range, and each stat itself has several textures.
- ///
- public readonly List FireFrames = new();
-
- public readonly Color? FireColor;
-
- internal Explosion(ExplosionEvent args, ExplosionPrototype type, EntityUid lightEntity, IResourceCache resCache)
- {
- Map = args.Epicenter.MapId;
- SpaceTiles = args.SpaceTiles;
- Tiles = args.Tiles;
- Intensity = args.Intensity;
- SpaceMatrix = args.SpaceMatrix;
- Explosionid = args.ExplosionId;
- FireColor = type.FireColor;
- LightEntity = lightEntity;
- SpaceTileSize = args.SpaceTileSize;
- IntensityPerState = type.IntensityPerState;
-
- var fireRsi = resCache.GetResource(type.TexturePath).RSI;
- foreach (var state in fireRsi)
- {
- FireFrames.Add(state.GetFrames(RSI.State.Direction.South));
- if (FireFrames.Count == type.FireStates)
- break;
- }
+ _overlayMan.RemoveOverlay();
}
}
diff --git a/Content.Client/Explosion/ExplosionVisualsComponent.cs b/Content.Client/Explosion/ExplosionVisualsComponent.cs
new file mode 100644
index 0000000000..953934b975
--- /dev/null
+++ b/Content.Client/Explosion/ExplosionVisualsComponent.cs
@@ -0,0 +1,25 @@
+using Content.Shared.Explosion;
+using Robust.Client.Graphics;
+
+namespace Content.Client.Explosion;
+
+[RegisterComponent]
+[ComponentReference(typeof(SharedExplosionVisualsComponent))]
+public sealed class ExplosionVisualsComponent : SharedExplosionVisualsComponent
+{
+ public EntityUid LightEntity;
+ ///
+ /// How long have we been drawing this explosion, starting from the time the explosion was fully drawn.
+ ///
+ public float Lifetime;
+
+ public float IntensityPerState;
+
+ ///
+ /// The textures used for the explosion fire effect. Each fire-state is associated with an explosion
+ /// intensity range, and each stat itself has several textures.
+ ///
+ public List FireFrames = new();
+
+ public Color? FireColor;
+}
diff --git a/Content.Server/Explosion/Components/ExplosionVisualsComponent.cs b/Content.Server/Explosion/Components/ExplosionVisualsComponent.cs
new file mode 100644
index 0000000000..eba85ca8f6
--- /dev/null
+++ b/Content.Server/Explosion/Components/ExplosionVisualsComponent.cs
@@ -0,0 +1,9 @@
+using Content.Shared.Explosion;
+
+namespace Content.Server.Explosion;
+
+[RegisterComponent]
+[ComponentReference(typeof(SharedExplosionVisualsComponent))]
+public sealed class ExplosionVisualsComponent : SharedExplosionVisualsComponent
+{
+}
diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs
index 9fdf3cab55..0497790775 100644
--- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs
+++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs
@@ -1,8 +1,10 @@
using System.Linq;
+using Content.Shared.CCVar;
using Content.Shared.Damage;
using Content.Shared.Explosion;
using Content.Shared.Maps;
using Content.Shared.Physics;
+using Content.Shared.Spawners.Components;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics;
@@ -15,16 +17,6 @@ namespace Content.Server.Explosion.EntitySystems;
public sealed partial class ExplosionSystem : EntitySystem
{
- ///
- /// Used to identify explosions when communicating with the client. Might be needed if more than one explosion is spawned in a single tick.
- ///
- ///
- /// Overflowing back to 0 should cause no issue, as long as you don't have more than 256 explosions happening in a single tick.
- ///
- private int _explosionCounter = 0;
- // maybe should just use a UID/explosion-entity and a state to convey information?
- // but then need to ignore PVS? Eeehh this works well enough for now.
-
///
/// Used to limit explosion processing time. See .
///
@@ -63,6 +55,7 @@ public sealed partial class ExplosionSystem : EntitySystem
if (_activeExplosion?.Epicenter.MapId != ev.Map)
return;
+ QueueDel(_activeExplosion.VisualEnt);
_activeExplosion = null;
_nodeGroupSystem.PauseUpdating = false;
_pathfindingSystem.PauseUpdating = false;
@@ -103,7 +96,6 @@ public sealed partial class ExplosionSystem : EntitySystem
if (_activeExplosion == null)
continue;
- _explosionCounter++;
_previousTileIteration = 0;
// just a lil nap
@@ -131,14 +123,20 @@ public sealed partial class ExplosionSystem : EntitySystem
// has the explosion finished processing?
if (_activeExplosion.FinishedProcessing)
- _activeExplosion = null;
+ {
+ var comp = EnsureComp(_activeExplosion.VisualEnt);
+ comp.Lifetime = _cfg.GetCVar(CCVars.ExplosionPersistence);
+ _appearance.SetData(_activeExplosion.VisualEnt, ExplosionAppearanceData.Progress, int.MaxValue);
+ _activeExplosion = null;
+ }
#if EXCEPTION_TOLERANCE
}
catch (Exception e)
{
// Ensure the system does not get stuck in an error-loop.
+ if (_activeExplosion != null)
+ QueueDel(_activeExplosion.VisualEnt);
_activeExplosion = null;
- RaiseNetworkEvent(new ExplosionOverlayUpdateEvent(_explosionCounter, int.MaxValue));
_nodeGroupSystem.PauseUpdating = false;
_pathfindingSystem.PauseUpdating = false;
throw;
@@ -151,21 +149,13 @@ public sealed partial class ExplosionSystem : EntitySystem
// we have finished processing our tiles. Is there still an ongoing explosion?
if (_activeExplosion != null)
{
- // update the client explosion overlays. This ensures that the fire-effects sync up with the entities currently being damaged.
- if (_previousTileIteration == _activeExplosion.CurrentIteration)
- return;
-
- _previousTileIteration = _activeExplosion.CurrentIteration;
- RaiseNetworkEvent(new ExplosionOverlayUpdateEvent(_explosionCounter, _previousTileIteration + 1));
+ _appearance.SetData(_activeExplosion.VisualEnt, ExplosionAppearanceData.Progress, _activeExplosion.CurrentIteration + 1);
return;
}
if (_explosionQueue.Count > 0)
return;
- // We have finished processing all explosions. Clear client explosion overlays
- RaiseNetworkEvent(new ExplosionOverlayUpdateEvent(_explosionCounter, int.MaxValue));
-
//wakey wakey
_nodeGroupSystem.PauseUpdating = false;
_pathfindingSystem.PauseUpdating = false;
@@ -586,6 +576,8 @@ sealed class Explosion
private readonly IEntityManager _entMan;
private readonly ExplosionSystem _system;
+ public readonly EntityUid VisualEnt;
+
///
/// Initialize a new instance for processing
///
@@ -601,8 +593,10 @@ sealed class Explosion
int maxTileBreak,
bool canCreateVacuum,
IEntityManager entMan,
- IMapManager mapMan)
+ IMapManager mapMan,
+ EntityUid visualEnt)
{
+ VisualEnt = visualEnt;
_system = system;
ExplosionType = explosionType;
_tileSetIntensity = tileSetIntensity;
diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs
index c4586d7004..881bfe2a4e 100644
--- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs
+++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs
@@ -313,7 +313,7 @@ public sealed partial class ExplosionSystem : EntitySystem
return (grids, referenceGrid, radius);
}
- public ExplosionEvent? GenerateExplosionPreview(SpawnExplosionEuiMsg.PreviewRequest request)
+ public ExplosionVisualsState? GenerateExplosionPreview(SpawnExplosionEuiMsg.PreviewRequest request)
{
var stopwatch = new Stopwatch();
stopwatch.Start();
@@ -332,7 +332,19 @@ public sealed partial class ExplosionSystem : EntitySystem
Logger.Info($"Generated explosion preview with {area} tiles in {stopwatch.Elapsed.TotalMilliseconds}ms");
- // the explosion event that **would** be sent to all clients, if it were a real explosion.
- return GetExplosionEvent(request.Epicenter, request.TypeId, spaceMatrix, spaceData, gridData.Values, iterationIntensity);
+ Dictionary>> tileLists = new();
+ foreach (var (grid, data) in gridData)
+ {
+ tileLists.Add(grid, data.TileLists);
+ }
+
+ return new ExplosionVisualsState(
+ request.Epicenter,
+ request.TypeId,
+ iterationIntensity,
+ spaceData?.TileLists,
+ tileLists, spaceMatrix,
+ spaceData?.TileSize ?? DefaultTileSize
+ );
}
}
diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Visuals.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Visuals.cs
new file mode 100644
index 0000000000..9892b36896
--- /dev/null
+++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Visuals.cs
@@ -0,0 +1,58 @@
+using Content.Shared.Explosion;
+using Robust.Server.GameObjects;
+using Robust.Shared.GameStates;
+using Robust.Shared.Map;
+
+namespace Content.Server.Explosion.EntitySystems;
+
+// This part of the system handled send visual / overlay data to clients.
+public sealed partial class ExplosionSystem : EntitySystem
+{
+ public void InitVisuals()
+ {
+ SubscribeLocalEvent(OnGetState);
+ }
+
+ private void OnGetState(EntityUid uid, ExplosionVisualsComponent component, ref ComponentGetState args)
+ {
+ args.State = new ExplosionVisualsState(
+ component.Epicenter,
+ component.ExplosionType,
+ component.Intensity,
+ component.SpaceTiles,
+ component.Tiles,
+ component.SpaceMatrix,
+ component.SpaceTileSize);
+ }
+
+ ///
+ /// Constructor for the shared using the server-exclusive explosion classes.
+ ///
+ private EntityUid CreateExplosionVisualEntity(MapCoordinates epicenter, string prototype, Matrix3 spaceMatrix, ExplosionSpaceTileFlood? spaceData, IEnumerable gridData, List iterationIntensity)
+ {
+ var explosionEntity = Spawn(null, MapCoordinates.Nullspace);
+ var comp = AddComp(explosionEntity);
+
+ foreach (var grid in gridData)
+ {
+ comp.Tiles.Add(grid.Grid.Owner, grid.TileLists);
+ }
+
+ comp.SpaceTiles = spaceData?.TileLists;
+ comp.Epicenter = epicenter;
+ comp.ExplosionType = prototype;
+ comp.Intensity = iterationIntensity;
+ comp.SpaceMatrix = spaceMatrix;
+ comp.SpaceTileSize = spaceData?.TileSize ?? DefaultTileSize;
+ Dirty(comp);
+
+ // Light, sound & visuals may extend well beyond normal PVS range. In principle, this should probably still be
+ // restricted to something like the same map, but whatever.
+ _pvsSys.AddGlobalOverride(explosionEntity);
+
+ var appearance = AddComp(explosionEntity);
+ _appearance.SetData(explosionEntity, ExplosionAppearanceData.Progress, 1, appearance);
+
+ return explosionEntity;
+ }
+}
diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
index d1cd0e5723..5b9eef5686 100644
--- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
+++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
@@ -11,6 +11,7 @@ using Content.Shared.Explosion;
using Content.Shared.GameTicking;
using Content.Shared.Inventory;
using Content.Shared.Throwing;
+using Robust.Server.GameStates;
using Robust.Server.Player;
using Robust.Shared.Audio;
using Robust.Shared.Configuration;
@@ -31,12 +32,14 @@ public sealed partial class ExplosionSystem : EntitySystem
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
+ [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly NodeGroupSystem _nodeGroupSystem = default!;
[Dependency] private readonly PathfindingSystem _pathfindingSystem = default!;
[Dependency] private readonly SharedCameraRecoilSystem _recoilSystem = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly ThrowingSystem _throwingSystem = default!;
+ [Dependency] private readonly PVSOverrideSystem _pvsSys = default!;
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
///
@@ -81,11 +84,14 @@ public sealed partial class ExplosionSystem : EntitySystem
SubscribeLocalEvent(OnAirtightDamaged);
SubscribeCvars();
InitAirtightMap();
+ InitVisuals();
}
private void OnReset(RoundRestartCleanupEvent ev)
{
_explosionQueue.Clear();
+ if (_activeExplosion !=null)
+ QueueDel(_activeExplosion.VisualEnt);
_activeExplosion = null;
_nodeGroupSystem.PauseUpdating = false;
_pathfindingSystem.PauseUpdating = false;
@@ -281,7 +287,7 @@ public sealed partial class ExplosionSystem : EntitySystem
var (area, iterationIntensity, spaceData, gridData, spaceMatrix) = results.Value;
- RaiseNetworkEvent(GetExplosionEvent(epicenter, type.ID, spaceMatrix, spaceData, gridData.Values, iterationIntensity));
+ var visualEnt = CreateExplosionVisualEntity(epicenter, type.ID, spaceMatrix, spaceData, gridData.Values, iterationIntensity);
// camera shake
CameraShake(iterationIntensity.Count * 2.5f, epicenter, totalIntensity);
@@ -306,23 +312,8 @@ public sealed partial class ExplosionSystem : EntitySystem
maxTileBreak,
canCreateVacuum,
EntityManager,
- _mapManager);
- }
-
- ///
- /// Constructor for the shared using the server-exclusive explosion classes.
- ///
- internal ExplosionEvent GetExplosionEvent(MapCoordinates epicenter, string id, Matrix3 spaceMatrix, ExplosionSpaceTileFlood? spaceData, IEnumerable gridData, List iterationIntensity)
- {
- var spaceTiles = spaceData?.TileLists;
-
- Dictionary>> tileLists = new();
- foreach (var grid in gridData)
- {
- tileLists.Add(grid.Grid.Owner, grid.TileLists);
- }
-
- return new ExplosionEvent(_explosionCounter, epicenter, id, iterationIntensity, spaceTiles, tileLists, spaceMatrix, spaceData?.TileSize ?? DefaultTileSize);
+ _mapManager,
+ visualEnt);
}
private void CameraShake(float range, MapCoordinates epicenter, float totalIntensity)
diff --git a/Content.Shared/Administration/SpawnExplosionEuiMsg.cs b/Content.Shared/Administration/SpawnExplosionEuiMsg.cs
index f9011bda30..38fd820996 100644
--- a/Content.Shared/Administration/SpawnExplosionEuiMsg.cs
+++ b/Content.Shared/Administration/SpawnExplosionEuiMsg.cs
@@ -40,9 +40,9 @@ public static class SpawnExplosionEuiMsg
{
public readonly float Slope;
public readonly float TotalIntensity;
- public readonly ExplosionEvent Explosion;
+ public readonly ExplosionVisualsState Explosion;
- public PreviewData(ExplosionEvent explosion, float slope, float totalIntensity)
+ public PreviewData(ExplosionVisualsState explosion, float slope, float totalIntensity)
{
Slope = slope;
TotalIntensity = totalIntensity;
diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs
index 0b897425b5..e74e1559b1 100644
--- a/Content.Shared/CCVar/CCVars.cs
+++ b/Content.Shared/CCVar/CCVars.cs
@@ -630,11 +630,10 @@ namespace Content.Shared.CCVar
CVarDef.Create("explosion.incremental_tile", false, CVar.SERVERONLY);
///
- /// Client-side explosion visuals: for how many seconds should an explosion stay on-screen once it has
- /// finished expanding?
+ /// This determines for how many seconds an explosion should stay visible once it has finished expanding.
///
public static readonly CVarDef ExplosionPersistence =
- CVarDef.Create("explosion.persistence", 0.3f, CVar.REPLICATED);
+ CVarDef.Create("explosion.persistence", 0.3f, CVar.SERVERONLY);
///
/// If an explosion covers a larger area than this number, the damaging/processing will always start during
diff --git a/Content.Shared/Explosion/ExplosionEvents.cs b/Content.Shared/Explosion/ExplosionEvents.cs
index dc7ed58dcb..5cd9d2e201 100644
--- a/Content.Shared/Explosion/ExplosionEvents.cs
+++ b/Content.Shared/Explosion/ExplosionEvents.cs
@@ -24,61 +24,3 @@ public sealed class GetExplosionResistanceEvent : EntityEventArgs, IInventoryRel
ExplotionPrototype = id;
}
}
-
-///
-/// An explosion event. Used for client side rendering.
-///
-[Serializable, NetSerializable]
-public sealed class ExplosionEvent : EntityEventArgs
-{
- public MapCoordinates Epicenter;
-
- public Dictionary>? SpaceTiles;
- public Dictionary>> Tiles;
-
- public List Intensity;
-
- public string TypeID;
-
- public Matrix3 SpaceMatrix;
-
- public int ExplosionId;
-
- public ushort SpaceTileSize;
-
- public ExplosionEvent(
- int explosionId,
- MapCoordinates epicenter,
- string typeID,
- List intensity,
- Dictionary>? spaceTiles,
- Dictionary>> tiles,
- Matrix3 spaceMatrix,
- ushort spaceTileSize)
- {
- Epicenter = epicenter;
- SpaceTiles = spaceTiles;
- Tiles = tiles;
- Intensity = intensity;
- TypeID = typeID;
- SpaceMatrix = spaceMatrix;
- ExplosionId = explosionId;
- SpaceTileSize = spaceTileSize;
- }
-}
-
-///
-/// Update visual rendering of the explosion to correspond to the servers processing of it.
-///
-[Serializable, NetSerializable]
-public sealed class ExplosionOverlayUpdateEvent : EntityEventArgs
-{
- public int Index;
- public int ExplosionId;
-
- public ExplosionOverlayUpdateEvent(int explosionId, int index)
- {
- Index = index;
- ExplosionId = explosionId;
- }
-}
diff --git a/Content.Shared/Explosion/SharedExplosionVisualsComponent.cs b/Content.Shared/Explosion/SharedExplosionVisualsComponent.cs
new file mode 100644
index 0000000000..f30f1767e5
--- /dev/null
+++ b/Content.Shared/Explosion/SharedExplosionVisualsComponent.cs
@@ -0,0 +1,56 @@
+using Robust.Shared.GameStates;
+using Robust.Shared.Map;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Explosion;
+
+///
+/// Component that is used to send explosion overlay/visual data to an abstract explosion entity.
+///
+[NetworkedComponent]
+public abstract class SharedExplosionVisualsComponent : Component
+{
+ public MapCoordinates Epicenter;
+ public Dictionary>? SpaceTiles;
+ public Dictionary>> Tiles = new();
+ public List Intensity = new();
+ public string ExplosionType = string.Empty;
+ public Matrix3 SpaceMatrix;
+ public ushort SpaceTileSize;
+}
+
+[Serializable, NetSerializable]
+public sealed class ExplosionVisualsState : ComponentState
+{
+ public MapCoordinates Epicenter;
+ public Dictionary>? SpaceTiles;
+ public Dictionary>> Tiles;
+ public List Intensity;
+ public string ExplosionType = string.Empty;
+ public Matrix3 SpaceMatrix;
+ public ushort SpaceTileSize;
+
+ public ExplosionVisualsState(
+ MapCoordinates epicenter,
+ string typeID,
+ List intensity,
+ Dictionary>? spaceTiles,
+ Dictionary>> tiles,
+ Matrix3 spaceMatrix,
+ ushort spaceTileSize)
+ {
+ Epicenter = epicenter;
+ SpaceTiles = spaceTiles;
+ Tiles = tiles;
+ Intensity = intensity;
+ ExplosionType = typeID;
+ SpaceMatrix = spaceMatrix;
+ SpaceTileSize = spaceTileSize;
+ }
+}
+
+[Serializable, NetSerializable]
+public enum ExplosionAppearanceData
+{
+ Progress, // iteration index tracker for explosions that are still expanding outwards,
+}