From: pathetic meowmeow Date: Wed, 14 Jan 2026 05:57:08 +0000 (-0500) Subject: Decouple gibbing from the body system (#42405) X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=fb133494cc3bedf82e9b60a434574e9029e04244;p=space-station-14.git Decouple gibbing from the body system (#42405) * Decouple gibbing from the body system * allow gibs that don't drop giblets * pass through user * prediction gon * comment * destructible * playpvs * very very very very very very very minor cleanup --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- diff --git a/Content.IntegrationTests/Tests/Body/GibTest.cs b/Content.IntegrationTests/Tests/Body/GibTest.cs deleted file mode 100644 index 4627c79f64..0000000000 --- a/Content.IntegrationTests/Tests/Body/GibTest.cs +++ /dev/null @@ -1,43 +0,0 @@ -#nullable enable -using Content.Server.Body.Systems; -using Robust.Shared.GameObjects; - -namespace Content.IntegrationTests.Tests.Body; - -[TestFixture] -public sealed class GibTest -{ - [Test] - public async Task TestGib() - { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); - var (server, client) = (pair.Server, pair.Client); - var map = await pair.CreateTestMap(); - - EntityUid target1 = default; - EntityUid target2 = default; - - await server.WaitAssertion(() => target1 = server.EntMan.Spawn("MobHuman", map.MapCoords)); - await server.WaitAssertion(() => target2 = server.EntMan.Spawn("MobHuman", map.MapCoords)); - await pair.WaitCommand($"setoutfit {server.EntMan.GetNetEntity(target1)} CaptainGear"); - await pair.WaitCommand($"setoutfit {server.EntMan.GetNetEntity(target2)} CaptainGear"); - - await pair.RunTicksSync(5); - var nuid1 = pair.ToClientUid(target1); - var nuid2 = pair.ToClientUid(target2); - Assert.That(client.EntMan.EntityExists(nuid1)); - Assert.That(client.EntMan.EntityExists(nuid2)); - - await server.WaitAssertion(() => server.System().GibBody(target1, gibOrgans: false)); - await server.WaitAssertion(() => server.System().GibBody(target2, gibOrgans: true)); - - await pair.RunTicksSync(5); - await pair.WaitCommand("dirty"); - await pair.RunTicksSync(5); - - Assert.That(!client.EntMan.EntityExists(nuid1)); - Assert.That(!client.EntMan.EntityExists(nuid2)); - - await pair.CleanReturnAsync(); - } -} diff --git a/Content.IntegrationTests/Tests/Gibbing/GibTest.cs b/Content.IntegrationTests/Tests/Gibbing/GibTest.cs new file mode 100644 index 0000000000..ee0f7a742d --- /dev/null +++ b/Content.IntegrationTests/Tests/Gibbing/GibTest.cs @@ -0,0 +1,36 @@ +#nullable enable +using Content.Shared.Gibbing; +using Robust.Shared.GameObjects; + +namespace Content.IntegrationTests.Tests.Body; + +[TestFixture] +public sealed class GibTest +{ + [Test] + public async Task TestGib() + { + await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var (server, client) = (pair.Server, pair.Client); + var map = await pair.CreateTestMap(); + + EntityUid target = default; + + await server.WaitAssertion(() => target = server.EntMan.Spawn("MobHuman", map.MapCoords)); + await pair.WaitCommand($"setoutfit {server.EntMan.GetNetEntity(target)} CaptainGear"); + + await pair.RunTicksSync(5); + var nuid = pair.ToClientUid(target); + Assert.That(client.EntMan.EntityExists(nuid)); + + await server.WaitAssertion(() => server.System().Gib(target)); + + await pair.RunTicksSync(5); + await pair.WaitCommand("dirty"); + await pair.RunTicksSync(5); + + Assert.That(!client.EntMan.EntityExists(nuid)); + + await pair.CleanReturnAsync(); + } +} diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs index f367acbd79..bae7776ef0 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs @@ -29,6 +29,7 @@ using Content.Shared.Damage.Components; using Content.Shared.Damage.Systems; using Content.Shared.Database; using Content.Shared.Electrocution; +using Content.Shared.Gibbing; using Content.Shared.Gravity; using Content.Shared.Interaction.Components; using Content.Shared.Inventory; @@ -92,6 +93,7 @@ public sealed partial class AdminVerbSystem [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly SuperBonkSystem _superBonkSystem = default!; [Dependency] private readonly SlipperySystem _slipperySystem = default!; + [Dependency] private readonly GibbingSystem _gibbing = default!; private readonly EntProtoId _actionViewLawsProtoId = "ActionViewLaws"; private readonly ProtoId _crewsimovLawset = "Crewsimov"; @@ -128,7 +130,7 @@ public sealed partial class AdminVerbSystem 4, 1, 2, args.Target, maxTileBreak: 0), // it gibs, damage doesn't need to be high. CancellationToken.None); - _bodySystem.GibBody(args.Target); + _gibbing.Gib(args.Target); }, Impact = LogImpact.Extreme, Message = string.Join(": ", explodeName, Loc.GetString("admin-smite-explode-description")) // we do this so the description tells admins the Text to run it via console. diff --git a/Content.Server/Anomaly/Effects/InnerBodyAnomalySystem.cs b/Content.Server/Anomaly/Effects/InnerBodyAnomalySystem.cs index d6039fac01..9c671d4db2 100644 --- a/Content.Server/Anomaly/Effects/InnerBodyAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/InnerBodyAnomalySystem.cs @@ -10,6 +10,7 @@ using Content.Shared.Anomaly.Effects; using Content.Shared.Body.Components; using Content.Shared.Chat; using Content.Shared.Database; +using Content.Shared.Gibbing; using Content.Shared.Mobs; using Content.Shared.Popups; using Content.Shared.Whitelist; @@ -25,7 +26,7 @@ public sealed class InnerBodyAnomalySystem : SharedInnerBodyAnomalySystem [Dependency] private readonly IAdminLogManager _adminLog = default!; [Dependency] private readonly AnomalySystem _anomaly = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly BodySystem _body = default!; + [Dependency] private readonly GibbingSystem _gibbing = default!; [Dependency] private readonly IChatManager _chat = default!; [Dependency] private readonly ISharedPlayerManager _player = default!; [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; @@ -134,7 +135,7 @@ public sealed class InnerBodyAnomalySystem : SharedInnerBodyAnomalySystem if (!TryComp(ent, out var body)) return; - _body.GibBody(ent, true, body, splatModifier: 5f); + _gibbing.Gib(ent.Owner); } private void OnSeverityChanged(Entity ent, ref AnomalySeverityChangedEvent args) diff --git a/Content.Server/Atmos/Rotting/RottingSystem.cs b/Content.Server/Atmos/Rotting/RottingSystem.cs index e83d80748b..ea45bf5ed6 100644 --- a/Content.Server/Atmos/Rotting/RottingSystem.cs +++ b/Content.Server/Atmos/Rotting/RottingSystem.cs @@ -3,6 +3,7 @@ using Content.Shared.Atmos; using Content.Shared.Atmos.Rotting; using Content.Shared.Body.Events; using Content.Shared.Damage.Systems; +using Content.Shared.Gibbing; using Content.Shared.Temperature.Components; using Robust.Server.Containers; using Robust.Shared.Physics.Components; @@ -21,12 +22,12 @@ public sealed class RottingSystem : SharedRottingSystem { base.Initialize(); - SubscribeLocalEvent(OnGibbed); + SubscribeLocalEvent(OnGibbed); SubscribeLocalEvent(OnTempIsRotting); } - private void OnGibbed(EntityUid uid, RottingComponent component, BeingGibbedEvent args) + private void OnGibbed(EntityUid uid, RottingComponent component, GibbedBeforeDeletionEvent args) { if (!TryComp(uid, out var physics)) return; diff --git a/Content.Server/Body/Systems/BodySystem.cs b/Content.Server/Body/Systems/BodySystem.cs index 957dea9d41..2627e97982 100644 --- a/Content.Server/Body/Systems/BodySystem.cs +++ b/Content.Server/Body/Systems/BodySystem.cs @@ -92,40 +92,4 @@ public sealed class BodySystem : SharedBodySystem var layers = HumanoidVisualLayersExtension.Sublayers(layer.Value); _humanoidSystem.SetLayersVisibility((bodyEnt, humanoid), layers, visible: false); } - - public override HashSet GibBody( - EntityUid bodyId, - bool gibOrgans = false, - BodyComponent? body = null, - bool launchGibs = true, - Vector2? splatDirection = null, - float splatModifier = 1, - Angle splatCone = default, - SoundSpecifier? gibSoundOverride = null - ) - { - if (!Resolve(bodyId, ref body, logMissing: false) - || TerminatingOrDeleted(bodyId) - || EntityManager.IsQueuedForDeletion(bodyId)) - { - return new HashSet(); - } - - if (HasComp(bodyId)) - return new HashSet(); - - var xform = Transform(bodyId); - if (xform.MapUid is null) - return new HashSet(); - - var gibs = base.GibBody(bodyId, gibOrgans, body, launchGibs: launchGibs, - splatDirection: splatDirection, splatModifier: splatModifier, splatCone:splatCone); - - var ev = new BeingGibbedEvent(gibs); - RaiseLocalEvent(bodyId, ref ev); - - QueueDel(bodyId); - - return gibs; - } } diff --git a/Content.Server/Destructible/DestructibleSystem.cs b/Content.Server/Destructible/DestructibleSystem.cs index 7ed736fffd..485ecdd954 100644 --- a/Content.Server/Destructible/DestructibleSystem.cs +++ b/Content.Server/Destructible/DestructibleSystem.cs @@ -2,7 +2,6 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Server.Administration.Logs; using Content.Server.Atmos.EntitySystems; -using Content.Server.Body.Systems; using Content.Server.Construction; using Content.Server.Destructible.Thresholds; using Content.Server.Destructible.Thresholds.Behaviors; @@ -16,6 +15,7 @@ using Content.Shared.Database; using Content.Shared.Destructible; using Content.Shared.Destructible.Thresholds.Triggers; using Content.Shared.FixedPoint; +using Content.Shared.Gibbing; using Content.Shared.Humanoid; using Content.Shared.Trigger.Systems; using JetBrains.Annotations; @@ -34,7 +34,7 @@ namespace Content.Server.Destructible [Dependency] public readonly AtmosphereSystem AtmosphereSystem = default!; [Dependency] public readonly AudioSystem AudioSystem = default!; - [Dependency] public readonly BodySystem BodySystem = default!; + [Dependency] public readonly GibbingSystem Gibbing = default!; [Dependency] public readonly ConstructionSystem ConstructionSystem = default!; [Dependency] public readonly ExplosionSystem ExplosionSystem = default!; [Dependency] public readonly StackSystem StackSystem = default!; diff --git a/Content.Server/Destructible/Thresholds/Behaviors/GibBehavior.cs b/Content.Server/Destructible/Thresholds/Behaviors/GibBehavior.cs index 9aa798a3d0..1addf96b62 100644 --- a/Content.Server/Destructible/Thresholds/Behaviors/GibBehavior.cs +++ b/Content.Server/Destructible/Thresholds/Behaviors/GibBehavior.cs @@ -14,10 +14,7 @@ namespace Content.Server.Destructible.Thresholds.Behaviors public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null) { - if (system.EntityManager.TryGetComponent(owner, out BodyComponent? body)) - { - system.BodySystem.GibBody(owner, _recursive, body); - } + system.Gibbing.Gib(owner, _recursive); } } } diff --git a/Content.Server/Forensics/Systems/ForensicsSystem.cs b/Content.Server/Forensics/Systems/ForensicsSystem.cs index 8370014ad6..078ce44e2b 100644 --- a/Content.Server/Forensics/Systems/ForensicsSystem.cs +++ b/Content.Server/Forensics/Systems/ForensicsSystem.cs @@ -13,6 +13,7 @@ using Content.Shared.DoAfter; using Content.Shared.Forensics; using Content.Shared.Forensics.Components; using Content.Shared.Forensics.Systems; +using Content.Shared.Gibbing; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Inventory; @@ -39,7 +40,7 @@ namespace Content.Server.Forensics // The solution entities are spawned on MapInit as well, so we have to wait for that to be able to set the DNA in the bloodstream correctly without ResolveSolution failing SubscribeLocalEvent(OnDNAInit, after: new[] { typeof(BloodstreamSystem) }); - SubscribeLocalEvent(OnBeingGibbed); + SubscribeLocalEvent(OnBeingGibbed); SubscribeLocalEvent(OnMeleeHit); SubscribeLocalEvent(OnRehydrated); SubscribeLocalEvent(OnAfterInteract, after: new[] { typeof(AbsorbentSystem) }); @@ -85,14 +86,14 @@ namespace Content.Server.Forensics } } - private void OnBeingGibbed(EntityUid uid, ForensicsComponent component, BeingGibbedEvent args) + private void OnBeingGibbed(Entity ent, ref GibbedBeforeDeletionEvent args) { string dna = Loc.GetString("forensics-dna-unknown"); - if (TryComp(uid, out DnaComponent? dnaComp) && dnaComp.DNA != null) + if (TryComp(ent, out DnaComponent? dnaComp) && dnaComp.DNA != null) dna = dnaComp.DNA; - foreach (EntityUid part in args.GibbedParts) + foreach (var part in args.Giblets) { var partComp = EnsureComp(part); partComp.DNAs.Add(dna); diff --git a/Content.Server/Gibbing/Systems/GibOnRoundEndSystem.cs b/Content.Server/Gibbing/Systems/GibOnRoundEndSystem.cs index d23832984f..8736908da6 100644 --- a/Content.Server/Gibbing/Systems/GibOnRoundEndSystem.cs +++ b/Content.Server/Gibbing/Systems/GibOnRoundEndSystem.cs @@ -2,12 +2,12 @@ using Content.Shared.Gibbing.Components; using Content.Shared.Mind; using Content.Shared.Objectives.Systems; -using Content.Server.Body.Systems; +using Content.Shared.Gibbing; namespace Content.Server.Gibbing.Systems; public sealed class GibOnRoundEndSystem : EntitySystem { - [Dependency] private readonly BodySystem _body = default!; + [Dependency] private readonly GibbingSystem _gibbing = default!; [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly SharedObjectivesSystem _objectives = default!; @@ -49,7 +49,7 @@ public sealed class GibOnRoundEndSystem : EntitySystem if (gibComp.SpawnProto != null) SpawnAtPosition(gibComp.SpawnProto, Transform(uid).Coordinates); - _body.GibBody(uid, splatModifier: 5f); + _gibbing.Gib(uid); } } } diff --git a/Content.Server/Guardian/GuardianSystem.cs b/Content.Server/Guardian/GuardianSystem.cs index 5f2597afef..16907dda8d 100644 --- a/Content.Server/Guardian/GuardianSystem.cs +++ b/Content.Server/Guardian/GuardianSystem.cs @@ -1,9 +1,9 @@ -using Content.Server.Body.Systems; using Content.Server.Popups; using Content.Shared.Actions; using Content.Shared.Damage.Systems; using Content.Shared.DoAfter; using Content.Shared.Examine; +using Content.Shared.Gibbing; using Content.Shared.Guardian; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; @@ -31,7 +31,7 @@ namespace Content.Server.Guardian [Dependency] private readonly SharedActionsSystem _actionSystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly BodySystem _bodySystem = default!; + [Dependency] private readonly GibbingSystem _gibbing = default!; [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; @@ -130,7 +130,7 @@ namespace Content.Server.Guardian // Ensure held items are dropped before deleting guardian. if (HasComp(guardian)) - _bodySystem.GibBody(component.HostedGuardian.Value); + _gibbing.Gib(component.HostedGuardian.Value); QueueDel(guardian); QueueDel(component.ActionEntity); diff --git a/Content.Server/ImmovableRod/ImmovableRodSystem.cs b/Content.Server/ImmovableRod/ImmovableRodSystem.cs index 646b5c97bb..a06d521a3b 100644 --- a/Content.Server/ImmovableRod/ImmovableRodSystem.cs +++ b/Content.Server/ImmovableRod/ImmovableRodSystem.cs @@ -5,6 +5,7 @@ using Content.Server.Popups; using Content.Shared.Body.Components; using Content.Shared.Damage.Systems; using Content.Shared.Examine; +using Content.Shared.Gibbing; using Content.Shared.Popups; using Robust.Shared.Audio.Systems; using Robust.Shared.Map; @@ -20,7 +21,7 @@ public sealed class ImmovableRodSystem : EntitySystem { [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly BodySystem _bodySystem = default!; + [Dependency] private readonly GibbingSystem _gibbing = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; @@ -125,7 +126,7 @@ public sealed class ImmovableRodSystem : EntitySystem return; } - _bodySystem.GibBody(ent, body: body); + _gibbing.Gib(ent); return; } diff --git a/Content.Server/Implants/ImplantedSystem.cs b/Content.Server/Implants/ImplantedSystem.cs index 7851758730..be477119a0 100644 --- a/Content.Server/Implants/ImplantedSystem.cs +++ b/Content.Server/Implants/ImplantedSystem.cs @@ -1,4 +1,5 @@ using Content.Shared.Body.Events; +using Content.Shared.Gibbing; using Content.Shared.Implants.Components; using Content.Shared.Storage; using Robust.Shared.Containers; @@ -11,7 +12,7 @@ public sealed partial class ImplanterSystem { SubscribeLocalEvent(OnImplantedInit); SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnGibbed); + SubscribeLocalEvent(OnGibbed); } private void OnImplantedInit(Entity ent, ref ComponentInit args) @@ -26,7 +27,7 @@ public sealed partial class ImplanterSystem _container.CleanContainer(ent.Comp.ImplantContainer); } - private void OnGibbed(Entity ent, ref BeingGibbedEvent args) + private void OnGibbed(Entity ent, ref GibbedBeforeDeletionEvent args) { // Drop the storage implant contents before the implants are deleted by the body being gibbed foreach (var implant in ent.Comp.ImplantContainer.ContainedEntities) diff --git a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs index 39a3ecb7bb..29c4d65d5b 100644 --- a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs @@ -1,9 +1,8 @@ -using Content.Server.Body.Systems; -using Content.Shared.Administration.Logs; -using Content.Shared.Body.Components; +using Content.Shared.Administration.Logs; using Content.Shared.Database; using Content.Shared.Destructible; using Content.Shared.DoAfter; +using Content.Shared.Gibbing; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Kitchen; @@ -24,7 +23,7 @@ namespace Content.Server.Kitchen.EntitySystems; public sealed class SharpSystem : EntitySystem { - [Dependency] private readonly BodySystem _bodySystem = default!; + [Dependency] private readonly GibbingSystem _gibbing = default!; [Dependency] private readonly SharedDestructibleSystem _destructibleSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; @@ -134,7 +133,7 @@ public sealed class SharpSystem : EntitySystem args.Args.User, popupType); - _bodySystem.GibBody(args.Args.Target.Value); // does nothing if ent can't be gibbed + _gibbing.Gib(args.Args.Target.Value); // does nothing if ent can't be gibbed _destructibleSystem.DestroyEntity(args.Args.Target.Value); args.Handled = true; diff --git a/Content.Server/Materials/MaterialReclaimerSystem.cs b/Content.Server/Materials/MaterialReclaimerSystem.cs index 15ff93833d..7af39b84a1 100644 --- a/Content.Server/Materials/MaterialReclaimerSystem.cs +++ b/Content.Server/Materials/MaterialReclaimerSystem.cs @@ -4,7 +4,6 @@ using Content.Server.Ghost; using Content.Server.Popups; using Content.Server.Stack; using Content.Server.Wires; -using Content.Shared.Body.Systems; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.EntitySystems; @@ -25,6 +24,7 @@ using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Utility; using System.Linq; +using Content.Shared.Gibbing; using Content.Shared.Humanoid; namespace Content.Server.Materials; @@ -39,7 +39,7 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem [Dependency] private readonly OpenableSystem _openable = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!; - [Dependency] private readonly SharedBodySystem _body = default!; //bobby + [Dependency] private readonly GibbingSystem _gibbing = default!; [Dependency] private readonly PuddleSystem _puddle = default!; [Dependency] private readonly StackSystem _stack = default!; [Dependency] private readonly SharedMindSystem _mind = default!; @@ -112,7 +112,7 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem Filter.PvsExcept(victim, entityManager: EntityManager), true); - _body.GibBody(victim, true); + _gibbing.Gib(victim); _appearance.SetData(entity.Owner, RecyclerVisuals.Bloody, true); args.Handled = true; } @@ -193,7 +193,7 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem _adminLogger.Add(LogType.Gib, logImpact, $"{ToPrettyString(item):victim} was gibbed by {ToPrettyString(uid):entity} "); if (component.ReclaimSolutions) SpawnChemicalsFromComposition(uid, item, completion, false, component, xform); - _body.GibBody(item, true); + _gibbing.Gib(item); _appearance.SetData(uid, RecyclerVisuals.Bloody, true); } else diff --git a/Content.Server/Mind/TransferMindOnGibSystem.cs b/Content.Server/Mind/TransferMindOnGibSystem.cs index ff71fa0560..81ebf96a87 100644 --- a/Content.Server/Mind/TransferMindOnGibSystem.cs +++ b/Content.Server/Mind/TransferMindOnGibSystem.cs @@ -1,5 +1,5 @@ using System.Linq; -using Content.Shared.Body.Events; +using Content.Shared.Gibbing; using Content.Shared.Mind; using Content.Shared.Mind.Components; using Content.Shared.Tag; @@ -21,19 +21,19 @@ public sealed class TransferMindOnGibSystem : EntitySystem /// public override void Initialize() { - SubscribeLocalEvent(OnGib); + SubscribeLocalEvent(OnGib); } - private void OnGib(EntityUid uid, TransferMindOnGibComponent component, BeingGibbedEvent args) + private void OnGib(Entity ent, ref GibbedBeforeDeletionEvent args) { - if (!_mindSystem.TryGetMind(uid, out var mindId, out var mind)) + if (!_mindSystem.TryGetMind(ent, out var mindId, out var mind)) return; - var validParts = args.GibbedParts.Where(p => _tag.HasTag(p, component.TargetTag)).ToHashSet(); + var validParts = args.Giblets.Where(p => _tag.HasTag(p, ent.Comp.TargetTag)).ToHashSet(); if (!validParts.Any()) return; - var ent = _random.Pick(validParts); - _mindSystem.TransferTo(mindId, ent, mind: mind); + var transfer = _random.Pick(validParts); + _mindSystem.TransferTo(mindId, transfer, mind: mind); } } diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs index 1441c2edc8..766c3e18dc 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs @@ -1002,7 +1002,7 @@ public sealed partial class ShuttleSystem { _logger.Add(LogType.Gib, LogImpact.Extreme, $"{ToPrettyString(ent):player} got gibbed by the shuttle" + $" {ToPrettyString(uid)} arriving from FTL at {xform.Coordinates:coordinates}"); - var gibs = _bobby.GibBody(ent, body: mob); + var gibs = _gibbing.Gib(ent); _immuneEnts.UnionWith(gibs); continue; } diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.cs index 86b97c40a6..43607ea1ea 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.cs @@ -1,5 +1,4 @@ using Content.Server.Administration.Logs; -using Content.Server.Body.Systems; using Content.Server.Buckle.Systems; using Content.Server.Parallax; using Content.Server.Procedural; @@ -9,6 +8,7 @@ using Content.Server.Station.Systems; using Content.Server.Stunnable; using Content.Shared.Buckle.Components; using Content.Shared.Damage.Systems; +using Content.Shared.Gibbing; using Content.Shared.Light.Components; using Content.Shared.Movement.Events; using Content.Shared.Salvage; @@ -42,7 +42,7 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem [Dependency] private readonly IPrototypeManager _protoManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly BiomeSystem _biomes = default!; - [Dependency] private readonly BodySystem _bobby = default!; + [Dependency] private readonly GibbingSystem _gibbing = default!; [Dependency] private readonly BuckleSystem _buckle = default!; [Dependency] private readonly DamageableSystem _damageSys = default!; [Dependency] private readonly DockingSystem _dockSystem = default!; diff --git a/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactCrusherSystem.cs b/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactCrusherSystem.cs index a2cd1eb715..5db6ba78e9 100644 --- a/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactCrusherSystem.cs +++ b/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactCrusherSystem.cs @@ -1,6 +1,7 @@ using Content.Server.Body.Systems; using Content.Server.Stack; using Content.Shared.Body.Components; +using Content.Shared.Gibbing; using Content.Shared.Storage.Components; using Content.Shared.Whitelist; using Content.Shared.Xenoarchaeology.Equipment; @@ -14,7 +15,7 @@ namespace Content.Server.Xenoarchaeology.Equipment.Systems; public sealed class ArtifactCrusherSystem : SharedArtifactCrusherSystem { [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly BodySystem _body = default!; + [Dependency] private readonly GibbingSystem _gibbing = default!; [Dependency] private readonly StackSystem _stack = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; @@ -44,7 +45,7 @@ public sealed class ArtifactCrusherSystem : SharedArtifactCrusherSystem if (!TryComp(contained, out var body)) Del(contained); - var gibs = _body.GibBody(contained, body: body, gibOrgans: true); + var gibs = _gibbing.Gib(contained); foreach (var gib in gibs) { ContainerSystem.Insert((gib, null, null, null), crusher.OutputContainer); diff --git a/Content.Shared/Body/Events/BeingGibbedEvent.cs b/Content.Shared/Body/Events/BeingGibbedEvent.cs deleted file mode 100644 index 7ab34f2e18..0000000000 --- a/Content.Shared/Body/Events/BeingGibbedEvent.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Content.Shared.Body.Events; - -/// -/// Raised when a body gets gibbed, before it is deleted. -/// -[ByRefEvent] -public readonly record struct BeingGibbedEvent(HashSet GibbedParts); diff --git a/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs b/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs index e86fd0c06b..2bce22c045 100644 --- a/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs +++ b/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs @@ -11,6 +11,7 @@ using Content.Shared.EntityEffects.Effects.Solution; using Content.Shared.FixedPoint; using Content.Shared.Fluids; using Content.Shared.Forensics.Components; +using Content.Shared.Gibbing; using Content.Shared.HealthExaminable; using Content.Shared.Mobs.Systems; using Content.Shared.Popups; @@ -50,7 +51,7 @@ public abstract class SharedBloodstreamSystem : EntitySystem SubscribeLocalEvent>(OnReactionAttempt); SubscribeLocalEvent(OnDamageChanged); SubscribeLocalEvent(OnHealthBeingExamined); - SubscribeLocalEvent(OnBeingGibbed); + SubscribeLocalEvent(OnBeingGibbed); SubscribeLocalEvent(OnApplyMetabolicMultiplier); SubscribeLocalEvent(OnRejuvenate); SubscribeLocalEvent(OnMetabolismExclusion); @@ -262,7 +263,7 @@ public abstract class SharedBloodstreamSystem : EntitySystem } } - private void OnBeingGibbed(Entity ent, ref BeingGibbedEvent args) + private void OnBeingGibbed(Entity ent, ref GibbedBeforeDeletionEvent args) { SpillAllSolutions(ent.AsNullable()); } diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs index 250f90db8f..7750040a07 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs @@ -5,11 +5,8 @@ using Content.Shared.Body.Organ; using Content.Shared.Body.Part; using Content.Shared.Body.Prototypes; using Content.Shared.DragDrop; -using Content.Shared.Gibbing.Components; -using Content.Shared.Gibbing.Events; -using Content.Shared.Gibbing.Systems; +using Content.Shared.Gibbing; using Content.Shared.Inventory; -using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Robust.Shared.Map; @@ -27,7 +24,6 @@ public partial class SharedBodySystem */ [Dependency] private readonly InventorySystem _inventory = default!; - [Dependency] private readonly GibbingSystem _gibbingSystem = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!; private const float GibletLaunchImpulse = 8; @@ -42,6 +38,7 @@ public partial class SharedBodySystem SubscribeLocalEvent(OnBodyInit); SubscribeLocalEvent(OnBodyMapInit); SubscribeLocalEvent(OnBodyCanDrag); + SubscribeLocalEvent(OnBeingGibbed); } private void OnBodyInserted(Entity ent, ref EntInsertedIntoContainerMessage args) @@ -283,56 +280,22 @@ public partial class SharedBodySystem } } - public virtual HashSet GibBody( - EntityUid bodyId, - bool gibOrgans = false, - BodyComponent? body = null, - bool launchGibs = true, - Vector2? splatDirection = null, - float splatModifier = 1, - Angle splatCone = default, - SoundSpecifier? gibSoundOverride = null) + private void OnBeingGibbed(Entity ent, ref BeingGibbedEvent args) { - var gibs = new HashSet(); - - if (!Resolve(bodyId, ref body, logMissing: false)) - return gibs; - - var root = GetRootPartOrNull(bodyId, body); - if (root != null && TryComp(root.Value.Entity, out GibbableComponent? gibbable)) - { - gibSoundOverride ??= gibbable.GibSound; - } - var parts = GetBodyChildren(bodyId, body).ToArray(); - gibs.EnsureCapacity(parts.Length); + var parts = GetBodyChildren(ent, ent).ToArray(); + args.Giblets.EnsureCapacity(args.Giblets.Capacity + parts.Length); foreach (var part in parts) { - - _gibbingSystem.TryGibEntityWithRef(bodyId, part.Id, GibType.Gib, GibContentsOption.Skip, ref gibs, - playAudio: false, launchGibs:true, launchDirection:splatDirection, launchImpulse: GibletLaunchImpulse * splatModifier, - launchImpulseVariance:GibletLaunchImpulseVariance, launchCone: splatCone); - - if (!gibOrgans) - continue; - foreach (var organ in GetPartOrgans(part.Id, part.Component)) { - _gibbingSystem.TryGibEntityWithRef(bodyId, organ.Id, GibType.Drop, GibContentsOption.Skip, - ref gibs, playAudio: false, launchImpulse: GibletLaunchImpulse * splatModifier, - launchImpulseVariance:GibletLaunchImpulseVariance, launchCone: splatCone); + args.Giblets.Add(organ.Id); } + PredictedQueueDel(part.Id); } - var bodyTransform = Transform(bodyId); - if (TryComp(bodyId, out var inventory)) + foreach (var item in _inventory.GetHandOrInventoryEntities(ent.Owner)) { - foreach (var item in _inventory.GetHandOrInventoryEntities(bodyId)) - { - SharedTransform.DropNextTo(item, (bodyId, bodyTransform)); - gibs.Add(item); - } + args.Giblets.Add(item); } - _audioSystem.PlayPredicted(gibSoundOverride, bodyTransform.Coordinates, null); - return gibs; } } diff --git a/Content.Shared/Devour/DevourSystem.cs b/Content.Shared/Devour/DevourSystem.cs index b0aca4f10d..26313765b0 100644 --- a/Content.Shared/Devour/DevourSystem.cs +++ b/Content.Shared/Devour/DevourSystem.cs @@ -1,9 +1,9 @@ using Content.Shared.Actions; -using Content.Shared.Body.Events; using Content.Shared.Body.Systems; using Content.Shared.Chemistry.Components; using Content.Shared.Devour.Components; using Content.Shared.DoAfter; +using Content.Shared.Gibbing; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Popups; @@ -33,7 +33,7 @@ public sealed class DevourSystem : EntitySystem SubscribeLocalEvent(OnShutdown); SubscribeLocalEvent(OnDevourAction); SubscribeLocalEvent(OnDoAfter); - SubscribeLocalEvent(OnGibContents); + SubscribeLocalEvent(OnGibContents); } private void OnStartup(Entity ent, ref ComponentStartup args) @@ -127,13 +127,11 @@ public sealed class DevourSystem : EntitySystem _audioSystem.PlayPredicted(ent.Comp.SoundDevour, ent.Owner, ent.Owner); } - private void OnGibContents(Entity ent, ref BeingGibbedEvent args) + private void OnGibContents(Entity ent, ref GibbedBeforeDeletionEvent args) { if (ent.Comp.StomachStorageWhitelist == null) return; - // For some reason we have two different systems that should handle gibbing, - // and for some another reason GibbingSystem, which should empty all containers, doesn't get involved in this process _containerSystem.EmptyContainer(ent.Comp.Stomach); } } diff --git a/Content.Shared/Gibbing/Components/GibbableComponent.cs b/Content.Shared/Gibbing/Components/GibbableComponent.cs deleted file mode 100644 index 9c7501f028..0000000000 --- a/Content.Shared/Gibbing/Components/GibbableComponent.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Content.Shared.Gibbing.Systems; -using Robust.Shared.Audio; -using Robust.Shared.GameStates; -using Robust.Shared.Prototypes; - -namespace Content.Shared.Gibbing.Components; - -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(GibbingSystem))] -public sealed partial class GibbableComponent : Component -{ - /// - /// Giblet entity prototypes to randomly select from when spawning additional giblets - /// - [DataField, AutoNetworkedField] - public List GibPrototypes = new(); - - /// - /// Number of giblet entities to spawn in addition to entity contents - /// - [DataField, AutoNetworkedField] - public int GibCount; - - /// - /// Sound to be played when this entity is gibbed, only played when playsound is true on the gibbing function - /// - [DataField, AutoNetworkedField] - public SoundSpecifier? GibSound = new SoundCollectionSpecifier("gib", AudioParams.Default.WithVariation(0.025f)); - - /// - /// Max distance giblets can be dropped from an entity when NOT using physics-based scattering - /// - [DataField, AutoNetworkedField] - public float GibScatterRange = 0.3f; -} diff --git a/Content.Shared/Gibbing/Events/GibbingEvents.cs b/Content.Shared/Gibbing/Events/GibbingEvents.cs deleted file mode 100644 index 949b10eab9..0000000000 --- a/Content.Shared/Gibbing/Events/GibbingEvents.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Robust.Shared.Serialization; - -namespace Content.Shared.Gibbing.Events; - - - -/// -/// Called just before we actually gib the target entity -/// -/// The entity being gibed -/// What type of gibbing is occuring -/// Containers we are allow to gib -/// Containers we are allow not allowed to gib -[ByRefEvent] public record struct AttemptEntityContentsGibEvent( - EntityUid Target, - GibContentsOption GibType, - List? AllowedContainers, - List? ExcludedContainers - ); - - -/// -/// Called just before we actually gib the target entity -/// -/// The entity being gibed -/// how many giblets to spawn -/// What type of gibbing is occuring -[ByRefEvent] public record struct AttemptEntityGibEvent(EntityUid Target, int GibletCount, GibType GibType); - -/// -/// Called immediately after we gib the target entity -/// -/// The entity being gibbed -/// Any entities that are spilled out (if any) -[ByRefEvent] public record struct EntityGibbedEvent(EntityUid Target, List DroppedEntities); - -[Serializable, NetSerializable] -public enum GibType : byte -{ - Skip, - Drop, - Gib, -} - -public enum GibContentsOption : byte -{ - Skip, - Drop, - Gib -} diff --git a/Content.Shared/Gibbing/Components/GibOnRoundEndComponent.cs b/Content.Shared/Gibbing/GibOnRoundEndComponent.cs similarity index 100% rename from Content.Shared/Gibbing/Components/GibOnRoundEndComponent.cs rename to Content.Shared/Gibbing/GibOnRoundEndComponent.cs diff --git a/Content.Shared/Gibbing/GibbingSystem.cs b/Content.Shared/Gibbing/GibbingSystem.cs new file mode 100644 index 0000000000..08c8fc268e --- /dev/null +++ b/Content.Shared/Gibbing/GibbingSystem.cs @@ -0,0 +1,84 @@ +using Content.Shared.Destructible; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Audio; +using Robust.Shared.Network; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Random; + +namespace Content.Shared.Gibbing; + +public sealed class GibbingSystem : EntitySystem +{ + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedDestructibleSystem _destructible = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + + private static readonly SoundSpecifier? GibSound = new SoundCollectionSpecifier("gib", AudioParams.Default.WithVariation(0.025f)); + + /// + /// Gibs an entity. + /// + /// The entity to gib. + /// Whether or not to drop giblets. + /// The user gibbing the entity, if any. + /// The set of giblets for this entity, if any. + public HashSet Gib(EntityUid ent, bool dropGiblets = true, EntityUid? user = null) + { + // user is unused because of prediction woes, eventually it'll be used for audio + + // BodySystem handles prediction rather poorly and causes client-sided bugs when we gib on the client + // This guard can be removed once it is gone and replaced by a prediction-safe system. + if (!_net.IsServer) + return new(); + + if (!_destructible.DestroyEntity(ent)) + return new(); + + _audio.PlayPvs(GibSound, ent); + + var gibbed = new HashSet(); + var beingGibbed = new BeingGibbedEvent(gibbed); + RaiseLocalEvent(ent, ref beingGibbed); + + if (dropGiblets) + { + foreach (var giblet in gibbed) + { + _transform.DropNextTo(giblet, ent); + FlingDroppedEntity(giblet); + } + } + + var beforeDeletion = new GibbedBeforeDeletionEvent(gibbed); + RaiseLocalEvent(ent, ref beforeDeletion); + + return gibbed; + } + + private const float GibletLaunchImpulse = 8; + private const float GibletLaunchImpulseVariance = 3; + + private void FlingDroppedEntity(EntityUid target) + { + var impulse = GibletLaunchImpulse + _random.NextFloat(GibletLaunchImpulseVariance); + var scatterVec = _random.NextAngle().ToVec() * impulse; + _physics.ApplyLinearImpulse(target, scatterVec); + } +} + +/// +/// Raised on an entity when it is being gibbed. +/// +/// If a component wants to provide giblets to scatter, add them to this hashset. +[ByRefEvent] +public readonly record struct BeingGibbedEvent(HashSet Giblets); + +/// +/// Raised on an entity when it is about to be deleted after being gibbed. +/// +/// The set of giblets this entity produced. +[ByRefEvent] +public readonly record struct GibbedBeforeDeletionEvent(HashSet Giblets); diff --git a/Content.Shared/Gibbing/Systems/GibbingSystem.cs b/Content.Shared/Gibbing/Systems/GibbingSystem.cs deleted file mode 100644 index 9eaadbb970..0000000000 --- a/Content.Shared/Gibbing/Systems/GibbingSystem.cs +++ /dev/null @@ -1,344 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Numerics; -using Content.Shared.Gibbing.Components; -using Content.Shared.Gibbing.Events; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Containers; -using Robust.Shared.Map; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; - -namespace Content.Shared.Gibbing.Systems; - -public sealed class GibbingSystem : EntitySystem -{ - [Dependency] private readonly SharedContainerSystem _containerSystem = default!; - [Dependency] private readonly SharedTransformSystem _transformSystem = default!; - [Dependency] private readonly SharedAudioSystem _audioSystem = default!; - [Dependency] private readonly SharedPhysicsSystem _physicsSystem = default!; - [Dependency] private readonly IRobustRandom _random = default!; - - //TODO: (future optimization) implement a system that "caps" giblet entities by deleting the oldest ones once we reach a certain limit, customizable via CVAR - - /// - /// Attempt to gib a specified entity. That entity must have a gibable components. This method is NOT recursive will only - /// work on the target and any entities it contains (depending on gibContentsOption) - /// - /// The outermost entity we care about, used to place the dropped items - /// Target entity/comp we wish to gib - /// What type of gibing are we performing - /// What type of gibing do we perform on any container contents? - /// a hashset containing all the entities that have been dropped/created - /// How much to multiply the random spread on dropped giblets(if we are dropping them!) - /// Should we play audio - /// A list of containerIds on the target that permit gibing - /// A list of containerIds on the target that DO NOT permit gibing - /// The cone we are launching giblets in (if we are launching them!) - /// Should we launch giblets or just drop them - /// The direction to launch giblets (if we are launching them!) - /// The impluse to launch giblets at(if we are launching them!) - /// /// Should we log if we are missing a gibbableComp when we call this function - /// The variation in giblet launch impulse (if we are launching them!) - /// True if successful, false if not - public bool TryGibEntity(Entity outerEntity, Entity gibbable, GibType gibType, - GibContentsOption gibContentsOption, - out HashSet droppedEntities, bool launchGibs = true, - Vector2 launchDirection = default, float launchImpulse = 0f, float launchImpulseVariance = 0f, - Angle launchCone = default, - float randomSpreadMod = 1.0f, bool playAudio = true, List? allowedContainers = null, - List? excludedContainers = null, bool logMissingGibable = false) - { - droppedEntities = new(); - return TryGibEntityWithRef(outerEntity, gibbable, gibType, gibContentsOption, ref droppedEntities, - launchGibs, launchDirection, launchImpulse, launchImpulseVariance, launchCone, randomSpreadMod, playAudio, - allowedContainers, excludedContainers, logMissingGibable); - } - - - /// - /// Attempt to gib a specified entity. That entity must have a gibable components. This method is NOT recursive will only - /// work on the target and any entities it contains (depending on gibContentsOption) - /// - /// The outermost entity we care about, used to place the dropped items - /// Target entity/comp we wish to gib - /// What type of gibing are we performing - /// What type of gibing do we perform on any container contents? - /// a hashset containing all the entities that have been dropped/created - /// How much to multiply the random spread on dropped giblets(if we are dropping them!) - /// Should we play audio - /// A list of containerIds on the target that permit gibing - /// A list of containerIds on the target that DO NOT permit gibing - /// The cone we are launching giblets in (if we are launching them!) - /// Should we launch giblets or just drop them - /// The direction to launch giblets (if we are launching them!) - /// The impluse to launch giblets at(if we are launching them!) - /// The variation in giblet launch impulse (if we are launching them!) - /// Should we log if we are missing a gibbableComp when we call this function - /// True if successful, false if not - public bool TryGibEntityWithRef( - Entity outerEntity, - Entity gibbable, - GibType gibType, - GibContentsOption gibContentsOption, - ref HashSet droppedEntities, - bool launchGibs = true, - Vector2? launchDirection = null, - float launchImpulse = 0f, - float launchImpulseVariance = 0f, - Angle launchCone = default, - float randomSpreadMod = 1.0f, - bool playAudio = true, - List? allowedContainers = null, - List? excludedContainers = null, - bool logMissingGibable = false) - { - if (!Resolve(gibbable, ref gibbable.Comp, logMissing: false)) - { - DropEntity(gibbable, (outerEntity, Transform(outerEntity)), randomSpreadMod, ref droppedEntities, - launchGibs, launchDirection, launchImpulse, launchImpulseVariance, launchCone); - if (logMissingGibable) - { - Log.Warning($"{ToPrettyString(gibbable)} does not have a GibbableComponent! " + - $"This is not required but may cause issues contained items to not be dropped."); - } - - return false; - } - - if (gibType == GibType.Skip && gibContentsOption == GibContentsOption.Skip) - return true; - if (launchGibs) - { - randomSpreadMod = 0; - } - - HashSet validContainers = new(); - var gibContentsAttempt = - new AttemptEntityContentsGibEvent(gibbable, gibContentsOption, allowedContainers, excludedContainers); - RaiseLocalEvent(gibbable, ref gibContentsAttempt); - - foreach (var container in _containerSystem.GetAllContainers(gibbable)) - { - var valid = true; - if (allowedContainers != null) - valid = allowedContainers.Contains(container.ID); - if (excludedContainers != null) - valid = valid && !excludedContainers.Contains(container.ID); - if (valid) - validContainers.Add(container); - } - - switch (gibContentsOption) - { - case GibContentsOption.Skip: - break; - case GibContentsOption.Drop: - { - foreach (var container in validContainers) - { - foreach (var ent in container.ContainedEntities) - { - DropEntity(new Entity(ent, null), outerEntity, randomSpreadMod, - ref droppedEntities, launchGibs, - launchDirection, launchImpulse, launchImpulseVariance, launchCone); - } - } - - break; - } - case GibContentsOption.Gib: - { - foreach (var container in validContainers) - { - foreach (var ent in container.ContainedEntities) - { - GibEntity(new Entity(ent, null), outerEntity, randomSpreadMod, - ref droppedEntities, launchGibs, - launchDirection, launchImpulse, launchImpulseVariance, launchCone); - } - } - - break; - } - } - - switch (gibType) - { - case GibType.Skip: - break; - case GibType.Drop: - { - DropEntity(gibbable, outerEntity, randomSpreadMod, ref droppedEntities, launchGibs, - launchDirection, launchImpulse, launchImpulseVariance, launchCone); - break; - } - case GibType.Gib: - { - GibEntity(gibbable, outerEntity, randomSpreadMod, ref droppedEntities, launchGibs, - launchDirection, launchImpulse, launchImpulseVariance, launchCone); - break; - } - } - - if (playAudio) - { - _audioSystem.PlayPredicted(gibbable.Comp.GibSound, outerEntity, null); - } - - if (gibType == GibType.Gib) - PredictedQueueDel(gibbable.Owner); - return true; - } - - private void DropEntity(Entity gibbable, Entity parent, float randomSpreadMod, - ref HashSet droppedEntities, bool flingEntity, Vector2? scatterDirection, float scatterImpulse, - float scatterImpulseVariance, Angle scatterCone) - { - var gibCount = 0; - if (Resolve(gibbable, ref gibbable.Comp, logMissing: false)) - { - gibCount = gibbable.Comp.GibCount; - } - - if (!Resolve(parent, ref parent.Comp, logMissing: false)) - return; - - var gibAttemptEvent = new AttemptEntityGibEvent(gibbable, gibCount, GibType.Drop); - RaiseLocalEvent(gibbable, ref gibAttemptEvent); - switch (gibAttemptEvent.GibType) - { - case GibType.Skip: - return; - case GibType.Gib: - GibEntity(gibbable, parent, randomSpreadMod, ref droppedEntities, flingEntity, scatterDirection, - scatterImpulse, scatterImpulseVariance, scatterCone, deleteTarget: false); - return; - } - - _transformSystem.DropNextTo(gibbable.Owner, parent); - _transformSystem.SetWorldRotation(gibbable, _random.NextAngle()); - droppedEntities.Add(gibbable); - if (flingEntity) - { - FlingDroppedEntity(gibbable, scatterDirection, scatterImpulse, scatterImpulseVariance, scatterCone); - } - - var gibbedEvent = new EntityGibbedEvent(gibbable, new List {gibbable}); - RaiseLocalEvent(gibbable, ref gibbedEvent); - } - - private List GibEntity(Entity gibbable, Entity parent, - float randomSpreadMod, - ref HashSet droppedEntities, bool flingEntity, Vector2? scatterDirection, float scatterImpulse, - float scatterImpulseVariance, Angle scatterCone, bool deleteTarget = true) - { - var localGibs = new List(); - var gibCount = 0; - var gibProtoCount = 0; - if (Resolve(gibbable, ref gibbable.Comp, logMissing: false)) - { - gibCount = gibbable.Comp.GibCount; - gibProtoCount = gibbable.Comp.GibPrototypes.Count; - } - - if (!Resolve(parent, ref parent.Comp, logMissing: false)) - return []; - - var gibAttemptEvent = new AttemptEntityGibEvent(gibbable, gibCount, GibType.Drop); - RaiseLocalEvent(gibbable, ref gibAttemptEvent); - switch (gibAttemptEvent.GibType) - { - case GibType.Skip: - return localGibs; - case GibType.Drop: - DropEntity(gibbable, parent, randomSpreadMod, ref droppedEntities, flingEntity, - scatterDirection, scatterImpulse, scatterImpulseVariance, scatterCone); - localGibs.Add(gibbable); - return localGibs; - } - - if (gibbable.Comp != null && gibProtoCount > 0) - { - if (flingEntity) - { - for (var i = 0; i < gibAttemptEvent.GibletCount; i++) - { - if (!TryCreateRandomGiblet(gibbable.Comp, parent.Comp.Coordinates, false, out var giblet, - randomSpreadMod)) - continue; - FlingDroppedEntity(giblet.Value, scatterDirection, scatterImpulse, scatterImpulseVariance, - scatterCone); - droppedEntities.Add(giblet.Value); - } - } - else - { - for (var i = 0; i < gibAttemptEvent.GibletCount; i++) - { - if (TryCreateRandomGiblet(gibbable.Comp, parent.Comp.Coordinates, false, out var giblet, - randomSpreadMod)) - droppedEntities.Add(giblet.Value); - } - } - } - - _transformSystem.AttachToGridOrMap(gibbable, Transform(gibbable)); - if (flingEntity) - { - FlingDroppedEntity(gibbable, scatterDirection, scatterImpulse, scatterImpulseVariance, scatterCone); - } - - var gibbedEvent = new EntityGibbedEvent(gibbable, localGibs); - RaiseLocalEvent(gibbable, ref gibbedEvent); - if (deleteTarget) - PredictedQueueDel(gibbable.Owner); - return localGibs; - } - - - public bool TryCreateRandomGiblet(Entity gibbable, [NotNullWhen(true)] out EntityUid? gibletEntity, - float randomSpreadModifier = 1.0f, bool playSound = true) - { - gibletEntity = null; - return Resolve(gibbable, ref gibbable.Comp) && TryCreateRandomGiblet(gibbable.Comp, Transform(gibbable).Coordinates, - playSound, out gibletEntity, randomSpreadModifier); - } - - public bool TryCreateAndFlingRandomGiblet(Entity gibbable, [NotNullWhen(true)] out EntityUid? gibletEntity, - Vector2 scatterDirection, float force, float scatterImpulseVariance, Angle scatterCone = default, - bool playSound = true) - { - gibletEntity = null; - if (!Resolve(gibbable, ref gibbable.Comp) || - !TryCreateRandomGiblet(gibbable.Comp, Transform(gibbable).Coordinates, playSound, out gibletEntity)) - return false; - FlingDroppedEntity(gibletEntity.Value, scatterDirection, force, scatterImpulseVariance, scatterCone); - return true; - } - - private void FlingDroppedEntity(EntityUid target, Vector2? direction, float impulse, float impulseVariance, - Angle scatterConeAngle) - { - var scatterAngle = direction?.ToAngle() ?? _random.NextAngle(); - var scatterVector = _random.NextAngle(scatterAngle - scatterConeAngle / 2, scatterAngle + scatterConeAngle / 2) - .ToVec() * (impulse + _random.NextFloat(impulseVariance)); - _physicsSystem.ApplyLinearImpulse(target, scatterVector); - } - - private bool TryCreateRandomGiblet(GibbableComponent gibbable, EntityCoordinates coords, - bool playSound, [NotNullWhen(true)] out EntityUid? gibletEntity, float? randomSpreadModifier = null) - { - gibletEntity = null; - if (gibbable.GibPrototypes.Count == 0) - return false; - gibletEntity = Spawn(gibbable.GibPrototypes[_random.Next(0, gibbable.GibPrototypes.Count)], - randomSpreadModifier == null - ? coords - : coords.Offset(_random.NextVector2(gibbable.GibScatterRange * randomSpreadModifier.Value))); - if (playSound) - _audioSystem.PlayPredicted(gibbable.GibSound, coords, null); - _transformSystem.SetWorldRotation(gibletEntity.Value, _random.NextAngle()); - return true; - } -} diff --git a/Content.Shared/Kitchen/SharedKitchenSpikeSystem.cs b/Content.Shared/Kitchen/SharedKitchenSpikeSystem.cs index 938aefc601..800cd2de71 100644 --- a/Content.Shared/Kitchen/SharedKitchenSpikeSystem.cs +++ b/Content.Shared/Kitchen/SharedKitchenSpikeSystem.cs @@ -1,11 +1,11 @@ using Content.Shared.Administration.Logs; -using Content.Shared.Body.Systems; using Content.Shared.Damage.Systems; using Content.Shared.Database; using Content.Shared.Destructible; using Content.Shared.DoAfter; using Content.Shared.DragDrop; using Content.Shared.Examine; +using Content.Shared.Gibbing; using Content.Shared.Hands; using Content.Shared.Humanoid; using Content.Shared.IdentityManagement; @@ -41,7 +41,7 @@ public sealed class SharedKitchenSpikeSystem : EntitySystem [Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!; - [Dependency] private readonly SharedBodySystem _bodySystem = default!; + [Dependency] private readonly GibbingSystem _gibbing = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedInteractionSystem _interaction = default!; @@ -317,7 +317,7 @@ public sealed class SharedKitchenSpikeSystem : EntitySystem // Gib the victim if there is nothing else to butcher. if (butcherable.SpawnedEntities.Count == 0) { - _bodySystem.GibBody(args.Target.Value, true); + _gibbing.Gib(args.Target.Value); var logSeverity = HasComp(args.Target) ? LogImpact.Extreme : LogImpact.High; diff --git a/Content.Shared/Magic/SharedMagicSystem.cs b/Content.Shared/Magic/SharedMagicSystem.cs index 50ef5f413a..82cae19ec1 100644 --- a/Content.Shared/Magic/SharedMagicSystem.cs +++ b/Content.Shared/Magic/SharedMagicSystem.cs @@ -1,12 +1,11 @@ using System.Numerics; -using Content.Shared.Body.Components; -using Content.Shared.Body.Systems; using Content.Shared.Charges.Components; using Content.Shared.Charges.Systems; using Content.Shared.Coordinates.Helpers; using Content.Shared.Doors.Components; using Content.Shared.Doors.Systems; using Content.Shared.Examine; +using Content.Shared.Gibbing; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; @@ -53,7 +52,7 @@ public abstract class SharedMagicSystem : EntitySystem [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly INetManager _net = default!; - [Dependency] private readonly SharedBodySystem _body = default!; + [Dependency] private readonly GibbingSystem _gibbing = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly SharedDoorSystem _door = default!; [Dependency] private readonly InventorySystem _inventory = default!; @@ -390,11 +389,7 @@ public abstract class SharedMagicSystem : EntitySystem var impulseVector = direction * 10000; _physics.ApplyLinearImpulse(ev.Target, impulseVector); - - if (!TryComp(ev.Target, out var body)) - return; - - _body.GibBody(ev.Target, true, body); + _gibbing.Gib(ev.Target); } // End Touch Spells diff --git a/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs b/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs index 9c28f5eb04..42da1c6d5d 100644 --- a/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs +++ b/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Administration.Logs; using Content.Shared.Body.Events; using Content.Shared.Containers.ItemSlots; using Content.Shared.Database; +using Content.Shared.Gibbing; using Content.Shared.Hands.EntitySystems; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; @@ -88,7 +89,7 @@ public abstract partial class SharedBorgSystem : EntitySystem SubscribeLocalEvent(OnRefreshMovementSpeedModifiers); SubscribeLocalEvent(OnUIOpenAttempt); SubscribeLocalEvent(OnMobStateChanged); - SubscribeLocalEvent(OnBeingGibbed); + SubscribeLocalEvent(OnBeingGibbed); SubscribeLocalEvent(OnGetDeadIC); SubscribeLocalEvent(OnGetUnrevivableIC); SubscribeLocalEvent(OnPowerCellSlotEmpty); @@ -293,7 +294,7 @@ public abstract partial class SharedBorgSystem : EntitySystem SetActive(chassis, false, user: args.Origin); } - private void OnBeingGibbed(Entity chassis, ref BeingGibbedEvent args) + private void OnBeingGibbed(Entity chassis, ref GibbedBeforeDeletionEvent args) { // Don't use the ItemSlotsSystem eject method since we don't want to play a sound and want we to eject the battery even if the slot is locked. if (TryComp(chassis, out var slotComp) && diff --git a/Content.Shared/Species/Systems/GibActionSystem.cs b/Content.Shared/Species/Systems/GibActionSystem.cs index bd7cb6baff..26c14f8699 100644 --- a/Content.Shared/Species/Systems/GibActionSystem.cs +++ b/Content.Shared/Species/Systems/GibActionSystem.cs @@ -1,6 +1,7 @@ using Content.Shared.Species.Components; using Content.Shared.Actions; using Content.Shared.Body.Systems; +using Content.Shared.Gibbing; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Popups; @@ -12,7 +13,7 @@ namespace Content.Shared.Species; public sealed partial class GibActionSystem : EntitySystem { [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; - [Dependency] private readonly SharedBodySystem _bodySystem = default!; + [Dependency] private readonly GibbingSystem _gibbing = default!; [Dependency] private readonly IPrototypeManager _protoManager = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; @@ -26,7 +27,7 @@ public sealed partial class GibActionSystem : EntitySystem private void OnMobStateChanged(EntityUid uid, GibActionComponent comp, MobStateChangedEvent args) { - // When the mob changes state, check if they're dead and give them the action if so. + // When the mob changes state, check if they're dead and give them the action if so. if (!TryComp(uid, out var mobState)) return; @@ -47,15 +48,15 @@ public sealed partial class GibActionSystem : EntitySystem // If they aren't given the action, remove it. _actionsSystem.RemoveAction(uid, comp.ActionEntity); } - + private void OnGibAction(EntityUid uid, GibActionComponent comp, GibActionEvent args) { // When they use the action, gib them. _popupSystem.PopupClient(Loc.GetString(comp.PopupText, ("name", uid)), uid, uid); - _bodySystem.GibBody(uid, true); + _gibbing.Gib(uid, user: args.Performer); } - - public sealed partial class GibActionEvent : InstantActionEvent { } + + public sealed partial class GibActionEvent : InstantActionEvent { } } diff --git a/Content.Shared/Trigger/Systems/GibOnTriggerSystem.cs b/Content.Shared/Trigger/Systems/GibOnTriggerSystem.cs index 237c925716..83b4790af9 100644 --- a/Content.Shared/Trigger/Systems/GibOnTriggerSystem.cs +++ b/Content.Shared/Trigger/Systems/GibOnTriggerSystem.cs @@ -1,4 +1,4 @@ -using Content.Shared.Body.Systems; +using Content.Shared.Gibbing; using Content.Shared.Inventory; using Content.Shared.Trigger.Components.Effects; @@ -6,7 +6,7 @@ namespace Content.Shared.Trigger.Systems; public sealed class GibOnTriggerSystem : XOnTriggerSystem { - [Dependency] private readonly SharedBodySystem _body = default!; + [Dependency] private readonly GibbingSystem _gibbing = default!; [Dependency] private readonly InventorySystem _inventory = default!; protected override void OnTrigger(Entity ent, EntityUid target, ref TriggerEvent args) @@ -20,7 +20,7 @@ public sealed class GibOnTriggerSystem : XOnTriggerSystem } } - _body.GibBody(target, true); + _gibbing.Gib(target, user: args.User); args.Handled = true; } } diff --git a/Resources/Prototypes/Body/Organs/Animal/animal.yml b/Resources/Prototypes/Body/Organs/Animal/animal.yml index 152c4c4112..b31904437e 100644 --- a/Resources/Prototypes/Body/Organs/Animal/animal.yml +++ b/Resources/Prototypes/Body/Organs/Animal/animal.yml @@ -1,5 +1,5 @@ - type: entity - id: BaseAnimalOrganUnGibbable + id: BaseAnimalOrgan parent: BaseItem abstract: true components: @@ -23,13 +23,6 @@ tags: - Meat -- type: entity - id: BaseAnimalOrgan - parent: BaseAnimalOrganUnGibbable - abstract: true - components: - - type: Gibbable - - type: entity id: OrganAnimalLungs parent: BaseAnimalOrgan diff --git a/Resources/Prototypes/Body/Organs/human.yml b/Resources/Prototypes/Body/Organs/human.yml index 4ac5faf67b..c12df45411 100644 --- a/Resources/Prototypes/Body/Organs/human.yml +++ b/Resources/Prototypes/Body/Organs/human.yml @@ -1,5 +1,5 @@ - type: entity - id: BaseHumanOrganUnGibbable + id: BaseHumanOrgan parent: BaseItem abstract: true components: @@ -27,16 +27,9 @@ tags: - Meat -- type: entity - id: BaseHumanOrgan - parent: BaseHumanOrganUnGibbable - abstract: true - components: - - type: Gibbable - - type: entity id: OrganHumanBrain - parent: BaseHumanOrganUnGibbable + parent: BaseHumanOrgan name: brain description: "The source of incredible, unending intelligence. Honk." components: diff --git a/Resources/Prototypes/Body/Parts/animal.yml b/Resources/Prototypes/Body/Parts/animal.yml index c9ea293cb6..9672b09711 100644 --- a/Resources/Prototypes/Body/Parts/animal.yml +++ b/Resources/Prototypes/Body/Parts/animal.yml @@ -23,7 +23,6 @@ - type: Tag tags: - Trash - - type: Gibbable - type: Extractable juiceSolution: reagents: diff --git a/Resources/Prototypes/Body/Parts/base.yml b/Resources/Prototypes/Body/Parts/base.yml index 331641f00b..8e41ba927d 100644 --- a/Resources/Prototypes/Body/Parts/base.yml +++ b/Resources/Prototypes/Body/Parts/base.yml @@ -9,7 +9,6 @@ - type: Damageable damageContainer: Biological - type: BodyPart - - type: Gibbable - type: ContainerContainer containers: bodypart: !type:Container