]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
explosion minor rework + fix (#21718)
authordeltanedas <39013340+deltanedas@users.noreply.github.com>
Sun, 19 Nov 2023 17:44:42 +0000 (17:44 +0000)
committerGitHub <noreply@github.com>
Sun, 19 Nov 2023 17:44:42 +0000 (04:44 +1100)
Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs
Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
Content.Server/Hands/Systems/HandsSystem.cs
Content.Server/Inventory/ServerInventorySystem.cs
Content.Server/Storage/EntitySystems/EntityStorageSystem.cs
Content.Server/Storage/EntitySystems/StorageSystem.cs
Content.Shared/Damage/Systems/DamageableSystem.cs
Content.Shared/Explosion/ExplosionEvents.cs
Content.Shared/Storage/Components/SharedEntityStorageComponent.cs
Resources/Prototypes/Body/Parts/slime.yml

index 91cbba648cb09503207daed0d95656f52c1fa454..617bc5527628ea375cdc54562f0c97d7382c090b 100644 (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);
     }
 
-    /// <summary>
-    ///     This function actually applies the explosion affects to an entity.
-    /// </summary>
-    private void ProcessEntity(
-        EntityUid uid,
-        MapCoordinates epicenter,
-        DamageSpecifier? damage,
-        float throwForce,
-        string id,
-        TransformComponent? xform)
+    private DamageSpecifier GetDamage(EntityUid uid,
+        string id, DamageSpecifier damage)
     {
-        // damage
-        if (damage != null && _damageQuery.TryGetComponent(uid, out var damageable))
-        {
-            // 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);
+        // 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)));
 
-            ev.DamageCoefficient = Math.Max(0, ev.DamageCoefficient);
+        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);
 
-            // TODO explosion entity
-            // Move explosion data into the existing explosion visuals entity
-            // Give each explosion a unique name, include in admin logs.
+            if (_containedEntities.Count == 0)
+                continue;
 
-            // 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))
+            _toDamage.EnsureCapacity(_toDamage.Count + _containedEntities.Count);
+            foreach (var contained in _containedEntities)
             {
-                _adminLogger.Add(LogType.Explosion, LogImpact.Medium,
-                    $"Explosion caused [{damage.Total}] damage to {ToPrettyString(uid):target} at {xform?.Coordinates}");
+                var newDamage = GetDamage(contained, prototype, damage);
+                _toDamage.Add((contained, newDamage));
             }
-
-            _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))
+    /// <summary>
+    ///     This function actually applies the explosion affects to an entity.
+    /// </summary>
+    private void ProcessEntity(
+        EntityUid uid,
+        MapCoordinates epicenter,
+        DamageSpecifier? originalDamage,
+        float throwForce,
+        string id,
+        TransformComponent? xform)
+    {
+        if (originalDamage != null)
         {
-            foreach (var container in containers.Containers.Values)
+            GetEntitiesToDamage(uid, originalDamage, id);
+            foreach (var (entity, damage) in _toDamage)
             {
-                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();
     }
 }
-
index fb8494c680f4c4a0d7bd3022656e6fcd876e96f2..be62aeb5ed6648847a08ec7ef039770808424263 100644 (file)
@@ -21,7 +21,6 @@ using Robust.Server.GameStates;
 using Robust.Server.Player;
 using Robust.Shared.Audio;
 using Robust.Shared.Configuration;
-using Robust.Shared.Containers;
 using Robust.Shared.Map;
 using Robust.Shared.Physics.Components;
 using Robust.Shared.Player;
@@ -53,11 +52,9 @@ public sealed partial class ExplosionSystem : EntitySystem
     [Dependency] private readonly SharedMapSystem _map = default!;
 
     private EntityQuery<TransformComponent> _transformQuery;
-    private EntityQuery<ContainerManagerComponent> _containersQuery;
     private EntityQuery<DamageableComponent> _damageQuery;
     private EntityQuery<PhysicsComponent> _physicsQuery;
     private EntityQuery<ProjectileComponent> _projectileQuery;
-    private EntityQuery<MindComponent> _mindQuery;
 
     /// <summary>
     ///     "Tile-size" for space when there are no nearby grids to use as a reference.
@@ -107,11 +104,9 @@ public sealed partial class ExplosionSystem : EntitySystem
         InitVisuals();
 
         _transformQuery = GetEntityQuery<TransformComponent>();
-        _containersQuery = GetEntityQuery<ContainerManagerComponent>();
         _damageQuery = GetEntityQuery<DamageableComponent>();
         _physicsQuery = GetEntityQuery<PhysicsComponent>();
         _projectileQuery = GetEntityQuery<ProjectileComponent>();
-        _mindQuery = GetEntityQuery<MindComponent>();
     }
 
     private void OnReset(RoundRestartCleanupEvent ev)
index e3e66995373101c8169d2c0888142dcc14380a08..5ceb4a8d6048cbf0c9c9f2f39a777f075217ef99 100644 (file)
@@ -8,6 +8,7 @@ using Content.Server.Stunnable;
 using Content.Shared.ActionBlocker;
 using Content.Shared.Body.Part;
 using Content.Shared.CombatMode;
+using Content.Shared.Explosion;
 using Content.Shared.Hands;
 using Content.Shared.Hands.Components;
 using Content.Shared.Hands.EntitySystems;
@@ -54,6 +55,8 @@ namespace Content.Server.Hands.Systems
 
             SubscribeLocalEvent<HandsComponent, ComponentGetState>(GetComponentState);
 
+            SubscribeLocalEvent<HandsComponent, BeforeExplodeEvent>(OnExploded);
+
             CommandBinds.Builder
                 .Bind(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem))
                 .Bind(ContentKeyFunctions.SmartEquipBackpack, InputCmdHandler.FromDelegate(HandleSmartEquipBackpack))
@@ -73,6 +76,15 @@ namespace Content.Server.Hands.Systems
             args.State = new HandsComponentState(hands);
         }
 
+        private void OnExploded(Entity<HandsComponent> ent, ref BeforeExplodeEvent args)
+        {
+            foreach (var hand in ent.Comp.Hands.Values)
+            {
+                if (hand.HeldEntity is {} uid)
+                    args.Contents.Add(uid);
+            }
+        }
+
         private void OnDisarmed(EntityUid uid, HandsComponent component, DisarmedEvent args)
         {
             if (args.Handled)
index f80a604ad5da58eb564f791a8f59ae33d91ff3eb..f8d4bd3a1fa98b69f29f6b4763b8d25a35173a3e 100644 (file)
@@ -1,5 +1,6 @@
 using Content.Server.Storage.EntitySystems;
 using Content.Shared.Clothing.Components;
+using Content.Shared.Explosion;
 using Content.Shared.Interaction.Events;
 using Content.Shared.Inventory;
 using Content.Shared.Inventory.Events;
@@ -15,11 +16,26 @@ namespace Content.Server.Inventory
         {
             base.Initialize();
 
+            SubscribeLocalEvent<InventoryComponent, BeforeExplodeEvent>(OnExploded);
+
             SubscribeLocalEvent<ClothingComponent, UseInHandEvent>(OnUseInHand);
 
             SubscribeNetworkEvent<OpenSlotStorageNetworkMessage>(OnOpenSlotStorage);
         }
 
+        private void OnExploded(Entity<InventoryComponent> ent, ref BeforeExplodeEvent args)
+        {
+            if (!TryGetContainerSlotEnumerator(ent, out var slots, ent.Comp))
+                return;
+
+            // explode each item in their inventory too
+            while (slots.MoveNext(out var slot))
+            {
+                if (slot.ContainedEntity != null)
+                    args.Contents.Add(slot.ContainedEntity.Value);
+            }
+        }
+
         private void OnUseInHand(EntityUid uid, ClothingComponent component, UseInHandEvent args)
         {
             if (args.Handled || !component.QuickEquip)
index 4bcad622c720d65999528f35ce2c494b4dc0028b..efb3734962d3f686a0d34e5bba444efdd05cf9b1 100644 (file)
@@ -4,6 +4,7 @@ using Content.Server.Construction;
 using Content.Server.Construction.Components;
 using Content.Server.Storage.Components;
 using Content.Shared.Destructible;
+using Content.Shared.Explosion;
 using Content.Shared.Foldable;
 using Content.Shared.Interaction;
 using Content.Shared.Lock;
@@ -46,6 +47,7 @@ public sealed class EntityStorageSystem : SharedEntityStorageSystem
 
         SubscribeLocalEvent<EntityStorageComponent, MapInitEvent>(OnMapInit);
         SubscribeLocalEvent<EntityStorageComponent, WeldableAttemptEvent>(OnWeldableAttempt);
+        SubscribeLocalEvent<EntityStorageComponent, BeforeExplodeEvent>(OnExploded);
 
         SubscribeLocalEvent<InsideEntityStorageComponent, InhaleLocationEvent>(OnInsideInhale);
         SubscribeLocalEvent<InsideEntityStorageComponent, ExhaleLocationEvent>(OnInsideExhale);
@@ -98,6 +100,15 @@ public sealed class EntityStorageSystem : SharedEntityStorageSystem
         }
     }
 
+    private void OnExploded(Entity<EntityStorageComponent> ent, ref BeforeExplodeEvent args)
+    {
+        if (ent.Comp.ExplosionDamageCoefficient <= 0)
+            return;
+
+        args.Contents.AddRange(ent.Comp.Contents.ContainedEntities);
+        args.DamageCoefficient *= ent.Comp.ExplosionDamageCoefficient;
+    }
+
     protected override void TakeGas(EntityUid uid, SharedEntityStorageComponent component)
     {
         if (!component.Airtight)
index d59006e75315c3e6c2655bee0b39360b25e2b194..a38577edfa5844023d7930bf3262d95c9c164c1d 100644 (file)
@@ -1,5 +1,6 @@
 using Content.Server.Administration.Managers;
 using Content.Shared.Administration;
+using Content.Shared.Explosion;
 using Content.Shared.Ghost;
 using Content.Shared.Hands;
 using Content.Shared.Lock;
@@ -27,6 +28,7 @@ public sealed partial class StorageSystem : SharedStorageSystem
         base.Initialize();
         SubscribeLocalEvent<StorageComponent, GetVerbsEvent<ActivationVerb>>(AddUiVerb);
         SubscribeLocalEvent<StorageComponent, BoundUIClosedEvent>(OnBoundUIClosed);
+        SubscribeLocalEvent<StorageComponent, BeforeExplodeEvent>(OnExploded);
 
         SubscribeLocalEvent<StorageFillComponent, MapInitEvent>(OnStorageFillMapInit);
     }
@@ -97,6 +99,11 @@ public sealed partial class StorageSystem : SharedStorageSystem
         }
     }
 
+    private void OnExploded(Entity<StorageComponent> ent, ref BeforeExplodeEvent args)
+    {
+        args.Contents.AddRange(ent.Comp.Container.ContainedEntities);
+    }
+
     /// <summary>
     ///     Opens the storage UI for an entity
     /// </summary>
index b36133276c249f6c679c679303266a14bbf23040..9337e79439f182c2e801146898e1964678ab93c8 100644 (file)
@@ -1,7 +1,9 @@
 using System.Linq;
+using Content.Shared.Administration.Logs;
 using Content.Shared.Damage.Prototypes;
 using Content.Shared.FixedPoint;
 using Content.Shared.Inventory;
+using Content.Shared.Mind.Components;
 using Content.Shared.Mobs.Components;
 using Content.Shared.Mobs.Systems;
 using Content.Shared.Radiation.Events;
@@ -16,12 +18,14 @@ namespace Content.Shared.Damage
     public sealed class DamageableSystem : EntitySystem
     {
         [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+        [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
         [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
         [Dependency] private readonly INetManager _netMan = default!;
         [Dependency] private readonly MobThresholdSystem _mobThreshold = default!;
 
         private EntityQuery<AppearanceComponent> _appearanceQuery;
         private EntityQuery<DamageableComponent> _damageableQuery;
+        private EntityQuery<MindContainerComponent> _mindContainerQuery;
 
         public override void Initialize()
         {
@@ -33,6 +37,7 @@ namespace Content.Shared.Damage
 
             _appearanceQuery = GetEntityQuery<AppearanceComponent>();
             _damageableQuery = GetEntityQuery<DamageableComponent>();
+            _mindContainerQuery = GetEntityQuery<MindContainerComponent>();
         }
 
         /// <summary>
index 37c956e165d5f9d85eaec9a70d2431755b679639..7b0cde48e6d1faaef286f0ab37748db456bb64d0 100644 (file)
@@ -1,6 +1,5 @@
+using Content.Shared.Damage;
 using Content.Shared.Inventory;
-using Robust.Shared.Map;
-using Robust.Shared.Serialization;
 
 namespace Content.Shared.Explosion;
 
@@ -20,3 +19,34 @@ public record struct GetExplosionResistanceEvent(string ExplosionPrototype) : II
 
     SlotFlags IInventoryRelayEvent.TargetSlots =>  ~SlotFlags.POCKET;
 }
+
+/// <summary>
+/// This event is raised directed at an entity that is about to receive damage from an explosion. It can be used to
+/// recursively add contained/child entities that should also receive damage. E.g., entities in a player's inventory
+/// or backpack. This event will be raised recursively so a matchbox in a backpack in a player's inventory
+/// will also receive this event.
+/// </summary>
+[ByRefEvent]
+public record struct BeforeExplodeEvent(DamageSpecifier Damage, string Id, List<EntityUid> Contents)
+{
+    /// <summary>
+    /// The damage that will be received by this entity. Note that the entity's explosion resistance has already been
+    /// used to modify this damage.
+    /// </summary>
+    public readonly DamageSpecifier Damage = Damage;
+
+    /// <summary>
+    /// ID of the explosion prototype.
+    /// </summary>
+    public readonly string Id = Id;
+
+    /// <summary>
+    /// Damage multiplier for modifying the damage that will get dealt to contained entities.
+    /// </summary>
+    public float DamageCoefficient = 1;
+
+    /// <summary>
+    /// Contained/child entities that should receive recursive explosion damage.
+    /// </summary>
+    public readonly List<EntityUid> Contents = Contents;
+}
index b4cd18f4d5c62c7eba05b48b1c2b623c9ee9b563..e70c59c9d67199f5ecd8750dfa9ab6cdaf5821ea 100644 (file)
@@ -124,6 +124,12 @@ public abstract partial class SharedEntityStorageComponent : Component
     /// </summary>
     [ViewVariables]
     public Container Contents = default!;
+
+    /// <summary>
+    /// Multiplier for explosion damage that gets applied to contained entities.
+    /// </summary>
+    [DataField]
+    public float ExplosionDamageCoefficient = 1;
 }
 
 [Serializable, NetSerializable]
index c11d7239500073edfff1bea51366659a0b20a418..4b0e94b008f2ba8b0e06ca81095acef404af4633 100644 (file)
@@ -1,7 +1,7 @@
 # TODO BODY: Part damage
 - type: entity
   id: PartSlime
-  parent: [BaseItem, PartBase]
+  parent: [BaseItem, BasePart]
   name: "slime body part"
   abstract: true