From 355d00a0f2529a5beac6426837fad129d45a41a6 Mon Sep 17 00:00:00 2001
From: deltanedas <39013340+deltanedas@users.noreply.github.com>
Date: Fri, 29 Mar 2024 23:46:05 +0000
Subject: [PATCH] combine same-tile explosions in the same tick (#25664)
* combine same-tile explosions in the same tick
* !
---------
Co-authored-by: deltanedas <@deltanedas:kde.org>
---
.../ExplosionSystem.Processing.cs | 26 +++++-
.../EntitySystems/ExplosionSystem.cs | 80 ++++++++++++-------
.../Explosion/ExplosionPrototype.cs | 7 ++
3 files changed, 82 insertions(+), 31 deletions(-)
diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs
index 60db6bdf5b..6e100fe656 100644
--- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs
+++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs
@@ -38,8 +38,15 @@ public sealed partial class ExplosionSystem
/// Queue for delayed processing of explosions. If there is an explosion that covers more than 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.
///
- private Queue> _explosionQueue = new();
+ private Queue _explosionQueue = new();
+
+ ///
+ /// All queued explosions that will be processed in .
+ /// These always have the same contents.
+ ///
+ private HashSet _queuedExplosions = new();
///
/// 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();
}
}
+
+///
+/// Data needed to spawn an explosion with .
+///
+public sealed class QueuedExplosion
+{
+ public MapCoordinates Epicenter;
+ public ExplosionPrototype Proto = new();
+ public float TotalIntensity, Slope, MaxTileIntensity, TileBreakScale;
+ public int MaxTileBreak;
+ public bool CanCreateVacuum;
+}
diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
index 46238fd7c5..23543895e6 100644
--- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
+++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
@@ -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);
}
///
@@ -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.
///
- 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);
diff --git a/Content.Shared/Explosion/ExplosionPrototype.cs b/Content.Shared/Explosion/ExplosionPrototype.cs
index ec9f2efa1e..df2fb18360 100644
--- a/Content.Shared/Explosion/ExplosionPrototype.cs
+++ b/Content.Shared/Explosion/ExplosionPrototype.cs
@@ -77,6 +77,13 @@ public sealed partial class ExplosionPrototype : IPrototype
[DataField("smallSoundIterationThreshold")]
public int SmallSoundIterationThreshold = 6;
+ ///
+ /// How far away another explosion in the same tick can be and be combined.
+ /// Total intensity is added to the original queued explosion.
+ ///
+ [DataField]
+ public float MaxCombineDistance = 1f;
+
[DataField("sound")]
public SoundSpecifier Sound = new SoundCollectionSpecifier("Explosion");
--
2.51.2