]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
combine same-tile explosions in the same tick (#25664)
authordeltanedas <39013340+deltanedas@users.noreply.github.com>
Fri, 29 Mar 2024 23:46:05 +0000 (23:46 +0000)
committerGitHub <noreply@github.com>
Fri, 29 Mar 2024 23:46:05 +0000 (10:46 +1100)
* combine same-tile explosions in the same tick

* !

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>
Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs
Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
Content.Shared/Explosion/ExplosionPrototype.cs

index 60db6bdf5bbafd688505df05865ba7c611b2522b..6e100fe656eb5e68504eec8a9d43881aee3b7985 100644 (file)
@@ -38,8 +38,15 @@ public sealed partial class ExplosionSystem
     ///     Queue for delayed processing of explosions. If there is an explosion that covers more than <see
     ///     cref="TilesPerTick"/> tiles, other explosions will actually be delayed slightly. Unless it's a station
     ///     nuke, this delay should never really be noticeable.
+    ///     This is also used to combine explosion intensities of the same kind.
     /// </summary>
-    private Queue<Func<Explosion?>> _explosionQueue = new();
+    private Queue<QueuedExplosion> _explosionQueue = new();
+
+    /// <summary>
+    /// All queued explosions that will be processed in <see cref="_explosionQueue"/>.
+    /// These always have the same contents.
+    /// </summary>
+    private HashSet<QueuedExplosion> _queuedExplosions = new();
 
     /// <summary>
     ///     The explosion currently being processed.
@@ -93,10 +100,11 @@ public sealed partial class ExplosionSystem
                 if (MathF.Max(MaxProcessingTime - 1, 0.1f) < Stopwatch.Elapsed.TotalMilliseconds)
                     break;
 
-                if (!_explosionQueue.TryDequeue(out var spawnNextExplosion))
+                if (!_explosionQueue.TryDequeue(out var queued))
                     break;
 
-                _activeExplosion = spawnNextExplosion();
+                _queuedExplosions.Remove(queued);
+                _activeExplosion = SpawnExplosion(queued);
 
                 // explosion spawning can be null if something somewhere went wrong. (e.g., negative explosion
                 // intensity).
@@ -867,3 +875,15 @@ sealed class Explosion
         _tileUpdateDict.Clear();
     }
 }
+
+/// <summary>
+/// Data needed to spawn an explosion with <see cref="ExplosionSystem.SpawnExplosion"/>.
+/// </summary>
+public sealed class QueuedExplosion
+{
+    public MapCoordinates Epicenter;
+    public ExplosionPrototype Proto = new();
+    public float TotalIntensity, Slope, MaxTileIntensity, TileBreakScale;
+    public int MaxTileBreak;
+    public bool CanCreateVacuum;
+}
index 46238fd7c5d7cfdde9cedff476ab1b1ec87ddc56..23543895e6fcff588b24b61bd7c5c9bf9995166c 100644 (file)
@@ -112,6 +112,7 @@ public sealed partial class ExplosionSystem : EntitySystem
     private void OnReset(RoundRestartCleanupEvent ev)
     {
         _explosionQueue.Clear();
+        _queuedExplosions.Clear();
         if (_activeExplosion != null)
             QueueDel(_activeExplosion.VisualEnt);
         _activeExplosion = null;
@@ -297,8 +298,36 @@ public sealed partial class ExplosionSystem : EntitySystem
         if (addLog) // dont log if already created a separate, more detailed, log.
             _adminLogger.Add(LogType.Explosion, LogImpact.High, $"Explosion ({typeId}) spawned at {epicenter:coordinates} with intensity {totalIntensity} slope {slope}");
 
-        _explosionQueue.Enqueue(() => SpawnExplosion(epicenter, type, totalIntensity,
-            slope, maxTileIntensity, tileBreakScale, maxTileBreak, canCreateVacuum));
+        // try to combine explosions on the same tile if they are the same type
+        foreach (var queued in _queuedExplosions)
+        {
+            // ignore different types or those on different maps
+            if (queued.Proto.ID != type.ID || queued.Epicenter.MapId != epicenter.MapId)
+                continue;
+
+            var dst2 = queued.Proto.MaxCombineDistance * queued.Proto.MaxCombineDistance;
+            var direction = queued.Epicenter.Position - epicenter.Position;
+            if (direction.LengthSquared() > dst2)
+                continue;
+
+            // they are close enough to combine so just add total intensity and prevent queuing another one
+            queued.TotalIntensity += totalIntensity;
+            return;
+        }
+
+        var boom = new QueuedExplosion()
+        {
+            Epicenter = epicenter,
+            Proto = type,
+            TotalIntensity = totalIntensity,
+            Slope = slope,
+            MaxTileIntensity = maxTileIntensity,
+            TileBreakScale = tileBreakScale,
+            MaxTileBreak = maxTileBreak,
+            CanCreateVacuum = canCreateVacuum
+        };
+        _explosionQueue.Enqueue(boom);
+        _queuedExplosions.Add(boom);
     }
 
     /// <summary>
@@ -306,32 +335,26 @@ public sealed partial class ExplosionSystem : EntitySystem
     ///     information about the affected tiles for the explosion system to process. It will also trigger the
     ///     camera shake and sound effect.
     /// </summary>
-    private Explosion? SpawnExplosion(MapCoordinates epicenter,
-        ExplosionPrototype type,
-        float totalIntensity,
-        float slope,
-        float maxTileIntensity,
-        float tileBreakScale,
-        int maxTileBreak,
-        bool canCreateVacuum)
+    private Explosion? SpawnExplosion(QueuedExplosion queued)
     {
-        if (!_mapManager.MapExists(epicenter.MapId))
+        var pos = queued.Epicenter;
+        if (!_mapManager.MapExists(pos.MapId))
             return null;
 
-        var results = GetExplosionTiles(epicenter, type.ID, totalIntensity, slope, maxTileIntensity);
+        var results = GetExplosionTiles(pos, queued.Proto.ID, queued.TotalIntensity, queued.Slope, queued.MaxTileIntensity);
 
         if (results == null)
             return null;
 
         var (area, iterationIntensity, spaceData, gridData, spaceMatrix) = results.Value;
 
-        var visualEnt = CreateExplosionVisualEntity(epicenter, type.ID, spaceMatrix, spaceData, gridData.Values, iterationIntensity);
+        var visualEnt = CreateExplosionVisualEntity(pos, queued.Proto.ID, spaceMatrix, spaceData, gridData.Values, iterationIntensity);
 
         // camera shake
-        CameraShake(iterationIntensity.Count * 4f, epicenter, totalIntensity);
+        CameraShake(iterationIntensity.Count * 4f, pos, queued.TotalIntensity);
 
         //For whatever bloody reason, sound system requires ENTITY coordinates.
-        var mapEntityCoords = EntityCoordinates.FromMap(_mapManager.GetMapEntityId(epicenter.MapId), epicenter, _transformSystem, EntityManager);
+        var mapEntityCoords = EntityCoordinates.FromMap(_mapManager.GetMapEntityId(pos.MapId), pos, _transformSystem, EntityManager);
 
         // play sound.
         // for the normal audio, we want everyone in pvs range
@@ -339,34 +362,35 @@ public sealed partial class ExplosionSystem : EntitySystem
         // this is capped to 30 because otherwise really huge bombs
         // will attempt to play regular audio for people who can't hear it anyway because the epicenter is so far away
         var audioRange = Math.Min(iterationIntensity.Count * 2, MaxExplosionAudioRange);
-        var filter = Filter.Pvs(epicenter).AddInRange(epicenter, audioRange);
-        var sound = iterationIntensity.Count < type.SmallSoundIterationThreshold
-            ? type.SmallSound
-            : type.Sound;
+        var filter = Filter.Pvs(pos).AddInRange(pos, audioRange);
+        var sound = iterationIntensity.Count < queued.Proto.SmallSoundIterationThreshold
+            ? queued.Proto.SmallSound
+            : queued.Proto.Sound;
 
         _audio.PlayStatic(sound, filter, mapEntityCoords, true, sound.Params);
 
         // play far sound
         // far sound should play for anyone who wasn't in range of any of the effects of the bomb
         var farAudioRange = iterationIntensity.Count * 5;
-        var farFilter = Filter.Empty().AddInRange(epicenter, farAudioRange).RemoveInRange(epicenter, audioRange);
-        var farSound = iterationIntensity.Count < type.SmallSoundIterationThreshold
-            ? type.SmallSoundFar
-            : type.SoundFar;
+        var farFilter = Filter.Empty().AddInRange(pos, farAudioRange).RemoveInRange(pos, audioRange);
+        var farSound = iterationIntensity.Count < queued.Proto.SmallSoundIterationThreshold
+            ? queued.Proto.SmallSoundFar
+            : queued.Proto.SoundFar;
 
         _audio.PlayGlobal(farSound, farFilter, true, farSound.Params);
 
         return new Explosion(this,
-            type,
+            queued.Proto,
             spaceData,
             gridData.Values.ToList(),
             iterationIntensity,
-            epicenter,
+            pos,
             spaceMatrix,
             area,
-            tileBreakScale,
-            maxTileBreak,
-            canCreateVacuum,
+            // TODO: instead of le copy paste fields refactor so it has QueuedExplosion as a field?
+            queued.TileBreakScale,
+            queued.MaxTileBreak,
+            queued.CanCreateVacuum,
             EntityManager,
             _mapManager,
             visualEnt);
index ec9f2efa1e173a3c2da2511fe742ea1d3f9b9ded..df2fb18360e0992497b745d8cf55c16d3b3b11a2 100644 (file)
@@ -77,6 +77,13 @@ public sealed partial class ExplosionPrototype : IPrototype
     [DataField("smallSoundIterationThreshold")]
     public int SmallSoundIterationThreshold = 6;
 
+    /// <summary>
+    /// How far away another explosion in the same tick can be and be combined.
+    /// Total intensity is added to the original queued explosion.
+    /// </summary>
+    [DataField]
+    public float MaxCombineDistance = 1f;
+
     [DataField("sound")]
     public SoundSpecifier Sound = new SoundCollectionSpecifier("Explosion");