explosion minor rework + fix (#21718)

This commit is contained in:
deltanedas
2023-11-19 17:44:42 +00:00
committed by GitHub
parent db76d85f36
commit f25773ffec
10 changed files with 148 additions and 62 deletions

View File

@@ -1,16 +1,11 @@
using System.Linq;
using System.Numerics;
using System.Reflection;
using Content.Server.Explosion.Components;
using Content.Shared.CCVar;
using Content.Shared.Damage;
using Content.Shared.Database;
using Content.Shared.Explosion;
using Content.Shared.Maps;
using Content.Shared.Mind.Components;
using Content.Shared.Physics;
using Content.Shared.Projectiles;
using Robust.Shared.Spawners;
using Content.Shared.Tag;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
@@ -55,6 +50,13 @@ public sealed partial class ExplosionSystem
/// </summary>
private int _previousTileIteration;
/// <summary>
/// This list is used when raising <see cref="BeforeExplodeEvent"/> to avoid allocating a new list per event.
/// </summary>
private readonly List<EntityUid> _containedEntities = new();
private readonly List<(EntityUid, DamageSpecifier)> _toDamage = new();
private List<EntityUid> _anchored = new();
private void OnMapChanged(MapChangedEvent ev)
@@ -84,8 +86,6 @@ public sealed partial class ExplosionSystem
Stopwatch.Restart();
var x = Stopwatch.Elapsed.TotalMilliseconds;
var availableTime = MaxProcessingTime;
var tilesRemaining = TilesPerTick;
while (tilesRemaining > 0 && MaxProcessingTime > Stopwatch.Elapsed.TotalMilliseconds)
{
@@ -369,64 +369,73 @@ public sealed partial class ExplosionSystem
return SpaceQueryCallback(ref state, in uid);
}
private DamageSpecifier GetDamage(EntityUid uid,
string id, DamageSpecifier damage)
{
// TODO Explosion Performance
// Cache this? I.e., instead of raising an event, check for a component?
var resistanceEv = new GetExplosionResistanceEvent(id);
RaiseLocalEvent(uid, ref resistanceEv);
resistanceEv.DamageCoefficient = Math.Max(0, resistanceEv.DamageCoefficient);
// ReSharper disable once CompareOfFloatsByEqualityOperator
if (resistanceEv.DamageCoefficient != 1)
damage *= resistanceEv.DamageCoefficient;
return damage;
}
private void GetEntitiesToDamage(EntityUid uid, DamageSpecifier originalDamage, string prototype)
{
_toDamage.Clear();
_toDamage.Add((uid, GetDamage(uid, prototype, originalDamage)));
for (var i = 0; i < _toDamage.Count; i++)
{
var (ent, damage) = _toDamage[i];
_containedEntities.Clear();
var ev = new BeforeExplodeEvent(damage, prototype, _containedEntities);
RaiseLocalEvent(ent, ref ev);
if (_containedEntities.Count == 0)
continue;
// ReSharper disable once CompareOfFloatsByEqualityOperator
if (ev.DamageCoefficient != 1)
damage *= ev.DamageCoefficient;
_toDamage.EnsureCapacity(_toDamage.Count + _containedEntities.Count);
foreach (var contained in _containedEntities)
{
var newDamage = GetDamage(contained, prototype, damage);
_toDamage.Add((contained, newDamage));
}
}
}
/// <summary>
/// This function actually applies the explosion affects to an entity.
/// </summary>
private void ProcessEntity(
EntityUid uid,
MapCoordinates epicenter,
DamageSpecifier? damage,
DamageSpecifier? originalDamage,
float throwForce,
string id,
TransformComponent? xform)
{
// damage
if (damage != null && _damageQuery.TryGetComponent(uid, out var damageable))
if (originalDamage != null)
{
// TODO Explosion Performance
// Cache this? I.e., instead of raising an event, check for a component?
var ev = new GetExplosionResistanceEvent(id);
RaiseLocalEvent(uid, ref ev);
ev.DamageCoefficient = Math.Max(0, ev.DamageCoefficient);
// TODO explosion entity
// Move explosion data into the existing explosion visuals entity
// Give each explosion a unique name, include in admin logs.
// TODO Explosion Performance
// This creates a new dictionary. Maybe we should just re-use a private local damage specifier and update it.
// Though most entities shouldn't have explosion resistance, so maybe its fine.
// ReSharper disable once CompareOfFloatsByEqualityOperator
if (ev.DamageCoefficient != 1)
damage *= ev.DamageCoefficient;
// Log damage to players. Damage is logged before dealing damage so that the position can be logged before
// the entity gets deleted.
if (_mindQuery.HasComponent(uid))
GetEntitiesToDamage(uid, originalDamage, id);
foreach (var (entity, damage) in _toDamage)
{
_adminLogger.Add(LogType.Explosion, LogImpact.Medium,
$"Explosion caused [{damage.Total}] damage to {ToPrettyString(uid):target} at {xform?.Coordinates}");
}
_damageableSystem.TryChangeDamage(uid, damage, ignoreResistances: true, damageable: damageable);
}
// if it's a container, try to damage all its contents
if (_containersQuery.TryGetComponent(uid, out var containers))
{
foreach (var container in containers.Containers.Values)
{
foreach (var ent in container.ContainedEntities)
{
// setting throw force to 0 to prevent offset items inside containers
ProcessEntity(ent, epicenter, damage, 0f, id, _transformQuery.GetComponent(uid));
}
// TODO EXPLOSIONS turn explosions into entities, and pass the the entity in as the damage origin.
_damageableSystem.TryChangeDamage(entity, damage, ignoreResistances: true);
}
}
// throw
if (xform != null // null implies anchored
if (xform != null // null implies anchored or in a container
&& !xform.Anchored
&& throwForce > 0
&& !EntityManager.IsQueuedForDeletion(uid)
@@ -442,10 +451,6 @@ public sealed partial class ExplosionSystem
_projectileQuery,
throwForce);
}
// TODO EXPLOSION puddle / flammable ignite?
// TODO EXPLOSION deaf/ear damage? other explosion effects?
}
/// <summary>
@@ -845,4 +850,3 @@ sealed class Explosion
_tileUpdateDict.Clear();
}
}