From d3380ba0c208873560ff275b05c29f5bdd1d834a Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Mon, 5 Feb 2024 23:10:46 -0800 Subject: [PATCH 1/8] Moving Gibbing rework out from medrefactor into it's own PR --- Content.Server/Body/Systems/BodySystem.cs | 29 +- .../Body/Systems/SharedBodySystem.Body.cs | 33 +- .../Gibbing/Components/GibbableComponent.cs | 24 ++ .../Gibbing/Events/GibbingEvents.cs | 33 ++ .../Gibbing/Systems/GibbingSystem.cs | 286 ++++++++++++++++++ .../Prototypes/Body/Organs/Animal/animal.yml | 3 + Resources/Prototypes/Body/Organs/human.yml | 3 + Resources/Prototypes/Body/Parts/animal.yml | 3 + Resources/Prototypes/Body/Parts/base.yml | 3 + Resources/Prototypes/Body/Parts/skeleton.yml | 3 + .../Prototypes/Body/Parts/terminator.yml | 3 + 11 files changed, 393 insertions(+), 30 deletions(-) create mode 100644 Content.Shared/Gibbing/Components/GibbableComponent.cs create mode 100644 Content.Shared/Gibbing/Events/GibbingEvents.cs create mode 100644 Content.Shared/Gibbing/Systems/GibbingSystem.cs diff --git a/Content.Server/Body/Systems/BodySystem.cs b/Content.Server/Body/Systems/BodySystem.cs index 1630d4cb10db..7a1b18f728ec 100644 --- a/Content.Server/Body/Systems/BodySystem.cs +++ b/Content.Server/Body/Systems/BodySystem.cs @@ -15,6 +15,7 @@ using Robust.Shared.Random; using Robust.Shared.Timing; using System.Numerics; +using Content.Shared.Gibbing.Components; using Content.Shared.Movement.Systems; using Robust.Shared.Audio.Systems; @@ -106,7 +107,9 @@ protected override void RemovePart( _humanoidSystem.SetLayersVisibility(bodyUid, layers, false, true, humanoid); } - public override HashSet GibBody(EntityUid bodyId, bool gibOrgans = false, BodyComponent? body = null, bool deleteItems = false, bool deleteBrain = false) + public override HashSet GibBody(EntityUid bodyId, bool gibOrgans = false, BodyComponent? body = null, + bool deleteItems = false, bool deleteBrain = false, GibbableComponent? gibbable = null, SoundSpecifier? gibSound = null, + bool launchGibs = true, Vector2? splatDirection = null, float splatModifier = 1, Angle splatCone = default) { if (!Resolve(bodyId, ref body, false)) return new HashSet(); @@ -118,28 +121,8 @@ public override HashSet GibBody(EntityUid bodyId, bool gibOrgans = fa if (xform.MapUid == null) return new HashSet(); - var gibs = base.GibBody(bodyId, gibOrgans, body, deleteItems, deleteBrain); - - var coordinates = xform.Coordinates; - var filter = Filter.Pvs(bodyId, entityManager: EntityManager); - var audio = AudioParams.Default.WithVariation(0.025f); - - _audio.PlayStatic(body.GibSound, filter, coordinates, true, audio); - - foreach (var entity in gibs) - { - if (deleteItems) - { - if (!HasComp(entity) || deleteBrain) - { - QueueDel(entity); - } - } - else - { - SharedTransform.SetCoordinates(entity, coordinates.Offset(_random.NextVector2(.3f))); - } - } + var gibs = base.GibBody(bodyId, gibOrgans, body, deleteItems, deleteBrain, launchGibs: launchGibs, + splatDirection: splatDirection, splatModifier: splatModifier, splatCone:splatCone); RaiseLocalEvent(bodyId, new BeingGibbedEvent(gibs)); QueueDel(bodyId); diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs index e5e4edddb070..5e13f8269420 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs @@ -5,8 +5,13 @@ 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.Inventory; using Content.Shared.Inventory.Events; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Robust.Shared.Map; using Robust.Shared.Utility; @@ -23,7 +28,10 @@ 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; + private const float GibletLaunchImpulseVariance = 3; private void InitializeBody() { // Body here to handle root body parts. @@ -264,28 +272,38 @@ public IEnumerable GetBodyAllSlots(EntityUid bodyId, BodyComponent } public virtual HashSet GibBody(EntityUid bodyId, bool gibOrgans = false, - BodyComponent? body = null, bool deleteItems = false, bool deleteBrain = false) + BodyComponent? body = null ,bool deleteItems = false, bool deleteBrain = false, GibbableComponent? gibbable = null, + SoundSpecifier? gibSound = null, bool launchGibs = true, Vector2? splatDirection = null, + float splatModifier = 1, Angle splatCone = default) { var gibs = new HashSet(); if (!Resolve(bodyId, ref body, false)) return gibs; + var root = GetRootPartOrNull(bodyId, body); + if (root != null && gibSound == null) + { + if (Resolve(root.Value.Entity, ref gibbable)) + gibSound = gibbable.GibSound; + } var parts = GetBodyChildren(bodyId, body).ToArray(); gibs.EnsureCapacity(parts.Length); - foreach (var part in parts) { - SharedTransform.AttachToGridOrMap(part.Id); - gibs.Add(part.Id); + + _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)) { - SharedTransform.AttachToGridOrMap(organ.Id); - gibs.Add(organ.Id); + _gibbingSystem.TryGibEntityWithRef(bodyId, organ.Id, GibType.Drop, GibContentsOption.Skip, + ref gibs, playAudio: false, launchImpulse: GibletLaunchImpulse* splatModifier, + launchImpulseVariance:GibletLaunchImpulseVariance, launchCone: splatCone); } } if(TryComp(bodyId, out var inventory)) @@ -296,6 +314,7 @@ public virtual HashSet GibBody(EntityUid bodyId, bool gibOrgans = fal gibs.Add(item); } } + _audioSystem.PlayPredicted(gibSound, Transform(bodyId).Coordinates, null, GibbableComponent.GibAudioParams); return gibs; } } diff --git a/Content.Shared/Gibbing/Components/GibbableComponent.cs b/Content.Shared/Gibbing/Components/GibbableComponent.cs new file mode 100644 index 000000000000..d023082c6e1a --- /dev/null +++ b/Content.Shared/Gibbing/Components/GibbableComponent.cs @@ -0,0 +1,24 @@ +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 +{ + [DataField(required:true), AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)] + public List GibPrototypes = new(); + + [DataField(required:true), AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)] + public int GibCount = 3; + + [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)] + public SoundSpecifier? GibSound = new SoundCollectionSpecifier("gib"); + + public const float GibScatterRange = 0.3f; + + public static readonly AudioParams GibAudioParams = AudioParams.Default.WithVariation(0.025f); + +} diff --git a/Content.Shared/Gibbing/Events/GibbingEvents.cs b/Content.Shared/Gibbing/Events/GibbingEvents.cs new file mode 100644 index 000000000000..a24298823b5b --- /dev/null +++ b/Content.Shared/Gibbing/Events/GibbingEvents.cs @@ -0,0 +1,33 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.Gibbing.Events; + +/// +/// 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/Systems/GibbingSystem.cs b/Content.Shared/Gibbing/Systems/GibbingSystem.cs new file mode 100644 index 000000000000..b733bd3e2a5e --- /dev/null +++ b/Content.Shared/Gibbing/Systems/GibbingSystem.cs @@ -0,0 +1,286 @@ +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 IPrototypeManager _prototypeManager = 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 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 + /// The gibable component + /// 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!) + /// True if successful, false if not + public bool TryGibEntity(EntityUid outerEntity, EntityUid target, GibType gibType, GibContentsOption gibContentsOption, + out HashSet droppedEntities, GibbableComponent? gibable = null, 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) + { + droppedEntities = new(); + return TryGibEntityWithRef(outerEntity, target, gibType, gibContentsOption, ref droppedEntities, gibable, + launchGibs, launchDirection, launchImpulse, launchImpulseVariance, launchCone, randomSpreadMod, playAudio, + allowedContainers, excludedContainers); + } + + + /// + /// 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 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 + /// The gibable component + /// 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!) + /// True if successful, false if not + public bool TryGibEntityWithRef(EntityUid outerEntity, EntityUid target, GibType gibType, GibContentsOption gibContentsOption, + ref HashSet droppedEntities, GibbableComponent? gibable = null, 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) + { + if (!Resolve(target, ref gibable, logMissing: false)) + return false; + if (gibType == GibType.Skip && gibContentsOption == GibContentsOption.Skip) + return true; + if (launchGibs) + { + randomSpreadMod = 0; + } + var parentXform = Transform(outerEntity); + HashSet validContainers = new(); + foreach (var container in _containerSystem.GetAllContainers(target)) + { + 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(ent, parentXform, randomSpreadMod, gibable, ref droppedEntities, launchGibs, + launchDirection, launchImpulse, launchImpulseVariance, launchCone); + } + } + break; + } + case GibContentsOption.Gib: + { + foreach (var container in _containerSystem.GetAllContainers(target)) + { + foreach (var ent in container.ContainedEntities) + { + GibEntity(ent, parentXform, randomSpreadMod, gibable,ref droppedEntities, launchGibs, + launchDirection, launchImpulse, launchImpulseVariance, launchCone); + } + } + break; + } + } + + switch (gibType) + { + case GibType.Skip: + break; + case GibType.Drop: + { + DropEntity(target, parentXform, randomSpreadMod, gibable, ref droppedEntities, launchGibs, + launchDirection, launchImpulse, launchImpulseVariance, launchCone); + break; + } + case GibType.Gib: + { + GibEntity(target, parentXform, randomSpreadMod, gibable, ref droppedEntities, launchGibs, + launchDirection, launchImpulse, launchImpulseVariance, launchCone); + break; + } + } + if (playAudio) + _audioSystem.PlayPredicted(gibable.GibSound, parentXform.Coordinates, null, GibbableComponent.GibAudioParams); + + if (gibType == GibType.Gib) + EntityManager.QueueDeleteEntity(target); + return true; + } + + private void DropEntity(EntityUid target, TransformComponent parentXform, float randomSpreadMod, GibbableComponent? gibable, + ref HashSet droppedEntities, bool flingEntity, Vector2? scatterDirection, float scatterImpulse, + float scatterImpulseVariance, Angle scatterCone) + { + if (!Resolve(target, ref gibable, logMissing: false)) + return; + var gibAttemptEvent = new AttemptEntityGibEvent(target, gibable.GibCount, GibType.Drop); + RaiseLocalEvent(target, ref gibAttemptEvent); + switch (gibAttemptEvent.GibType) + { + case GibType.Skip: + return; + case GibType.Gib: + GibEntity(target, parentXform, randomSpreadMod, gibable, ref droppedEntities, flingEntity,scatterDirection, + scatterImpulse, scatterImpulseVariance, scatterCone ,deleteTarget: false); + return; + } + _transformSystem.AttachToGridOrMap(target); + _transformSystem.SetCoordinates(target,parentXform.Coordinates); + _transformSystem.SetWorldRotation(target, _random.NextAngle()); + droppedEntities.Add(target); + if (flingEntity) + { + FlingDroppedEntity(target, scatterDirection, scatterImpulse, scatterImpulseVariance, scatterCone); + } + var gibbedEvent = new EntityGibbedEvent(target, new List{target}); + RaiseLocalEvent(target,ref gibbedEvent); + } + + private List GibEntity(EntityUid target, TransformComponent parentXform, float randomSpreadMod, + GibbableComponent? gibable, ref HashSet droppedEntities, bool flingEntity, Vector2? scatterDirection, float scatterImpulse, + float scatterImpulseVariance, Angle scatterCone, bool deleteTarget = true) + { + var localGibs = new List(); + if (!Resolve(target, ref gibable, logMissing: false)) + return localGibs; + var gibAttemptEvent = new AttemptEntityGibEvent(target, gibable.GibCount, GibType.Drop); + RaiseLocalEvent(target, ref gibAttemptEvent); + switch (gibAttemptEvent.GibType) + { + case GibType.Skip: + return localGibs; + case GibType.Drop: + DropEntity(target, parentXform, randomSpreadMod, gibable, ref droppedEntities, flingEntity, + scatterDirection, scatterImpulse, scatterImpulseVariance, scatterCone); + localGibs.Add(target); + return localGibs; + } + + if (gibable.GibPrototypes.Count > 0) + { + if (flingEntity) + { + for (var i = 0; i < gibAttemptEvent.GibletCount; i++) + { + if (!TryCreateRandomGiblet(gibable, parentXform.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(gibable, parentXform.Coordinates, false, out var giblet, randomSpreadMod)) + droppedEntities.Add(giblet.Value); + } + } + + } + _transformSystem.AttachToGridOrMap(target, Transform(target)); + if (flingEntity) + { + FlingDroppedEntity(target, scatterDirection, scatterImpulse, scatterImpulseVariance, scatterCone); + } + var gibbedEvent = new EntityGibbedEvent(target, localGibs); + RaiseLocalEvent(target,ref gibbedEvent); + if (deleteTarget) + EntityManager.QueueDeleteEntity(target); + return localGibs; + } + + + public bool TryCreateRandomGiblet(EntityUid target, [NotNullWhen(true)] out EntityUid? gibletEntity , + GibbableComponent? gibable = null, float randomSpreadModifier = 1.0f, bool playSound = true) + { + gibletEntity = null; + return Resolve(target, ref gibable) && TryCreateRandomGiblet(gibable, Transform(target).Coordinates, + playSound ,out gibletEntity, randomSpreadModifier); + } + + public bool TryCreateAndFlingRandomGiblet(EntityUid target, [NotNullWhen(true)] out EntityUid? gibletEntity , + Vector2 scatterDirection, float force, float scatterImpulseVariance, Angle scatterCone = default, GibbableComponent? gibable = null, bool playSound = true) + { + gibletEntity = null; + if (!Resolve(target, ref gibable) || + !TryCreateRandomGiblet(gibable, Transform(target).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 gibable, EntityCoordinates coords, + bool playSound, [NotNullWhen(true)] out EntityUid? gibletEntity, float? randomSpreadModifier = null) + { + gibletEntity = null; + if (gibable.GibPrototypes.Count == 0) + return false; + gibletEntity = EntityManager.SpawnEntity(gibable.GibPrototypes[_random.Next(0, gibable.GibPrototypes.Count)], + randomSpreadModifier == null ? coords : coords.Offset(_random.NextVector2(GibbableComponent.GibScatterRange * randomSpreadModifier.Value))); + if (playSound) + _audioSystem.PlayPredicted(gibable.GibSound, coords, null, GibbableComponent.GibAudioParams); + _transformSystem.SetWorldRotation(gibletEntity.Value, _random.NextAngle()); + return true; + } +} diff --git a/Resources/Prototypes/Body/Organs/Animal/animal.yml b/Resources/Prototypes/Body/Organs/Animal/animal.yml index 358fc74bca60..9051ffbaf8b7 100644 --- a/Resources/Prototypes/Body/Organs/Animal/animal.yml +++ b/Resources/Prototypes/Body/Organs/Animal/animal.yml @@ -9,6 +9,9 @@ sprite: Mobs/Species/Human/organs.rsi - type: StaticPrice price: 50 + - type: Gibbable + gibPrototypes: [] + gibCount: 3 - type: SolutionContainerManager solutions: food: diff --git a/Resources/Prototypes/Body/Organs/human.yml b/Resources/Prototypes/Body/Organs/human.yml index 6abf168a9e75..a41536ca1cc2 100644 --- a/Resources/Prototypes/Body/Organs/human.yml +++ b/Resources/Prototypes/Body/Organs/human.yml @@ -7,6 +7,9 @@ sprite: Mobs/Species/Human/organs.rsi - type: Organ - type: Food + - type: Gibbable + gibPrototypes: [] + gibCount: 3 - type: Extractable grindableSolutionName: organ - type: SolutionContainerManager diff --git a/Resources/Prototypes/Body/Parts/animal.yml b/Resources/Prototypes/Body/Parts/animal.yml index cf65fd21360d..185f208f15dd 100644 --- a/Resources/Prototypes/Body/Parts/animal.yml +++ b/Resources/Prototypes/Body/Parts/animal.yml @@ -23,6 +23,9 @@ - type: Tag tags: - Trash + - type: Gibbable + gibPrototypes: [ ] + gibCount: 3 - type: Extractable juiceSolution: reagents: diff --git a/Resources/Prototypes/Body/Parts/base.yml b/Resources/Prototypes/Body/Parts/base.yml index a27059f59f30..b5c87de96605 100644 --- a/Resources/Prototypes/Body/Parts/base.yml +++ b/Resources/Prototypes/Body/Parts/base.yml @@ -9,6 +9,9 @@ - type: Damageable damageContainer: Biological - type: BodyPart + - type: Gibbable + gibPrototypes: [] + gibCount: 3 - type: ContainerContainer containers: bodypart: !type:Container diff --git a/Resources/Prototypes/Body/Parts/skeleton.yml b/Resources/Prototypes/Body/Parts/skeleton.yml index 3f41f88ee549..29400da5e918 100644 --- a/Resources/Prototypes/Body/Parts/skeleton.yml +++ b/Resources/Prototypes/Body/Parts/skeleton.yml @@ -14,6 +14,9 @@ ents: [] - type: StaticPrice price: 20 + - type: Gibbable + gibPrototypes: [ ] + gibCount: 3 - type: Tag tags: - Trash diff --git a/Resources/Prototypes/Body/Parts/terminator.yml b/Resources/Prototypes/Body/Parts/terminator.yml index dec0c99f7c20..379c6f9293f3 100644 --- a/Resources/Prototypes/Body/Parts/terminator.yml +++ b/Resources/Prototypes/Body/Parts/terminator.yml @@ -16,6 +16,9 @@ containers: bodypart: !type:Container ents: [] + - type: Gibbable + gibPrototypes: [ ] + gibCount: 3 - type: StaticPrice price: 200 From 2a9fbe17bc15b26724d76e645ee996233ea4bff2 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Mon, 5 Feb 2024 23:37:29 -0800 Subject: [PATCH 2/8] Re-enabled warning for missing gibbable on TryGibEntity --- Content.Shared/Gibbing/Systems/GibbingSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Gibbing/Systems/GibbingSystem.cs b/Content.Shared/Gibbing/Systems/GibbingSystem.cs index b733bd3e2a5e..d577f8dfc64b 100644 --- a/Content.Shared/Gibbing/Systems/GibbingSystem.cs +++ b/Content.Shared/Gibbing/Systems/GibbingSystem.cs @@ -80,7 +80,7 @@ public bool TryGibEntityWithRef(EntityUid outerEntity, EntityUid target, GibType 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) { - if (!Resolve(target, ref gibable, logMissing: false)) + if (!Resolve(target, ref gibable, logMissing: true)) return false; if (gibType == GibType.Skip && gibContentsOption == GibContentsOption.Skip) return true; From cfe7f9a6be801386e11f57f6014006a455f63f09 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Mon, 5 Feb 2024 23:56:04 -0800 Subject: [PATCH 3/8] Implemented better logic for gibbing failover and better logging --- .../Gibbing/Systems/GibbingSystem.cs | 45 ++++++++++++++----- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/Content.Shared/Gibbing/Systems/GibbingSystem.cs b/Content.Shared/Gibbing/Systems/GibbingSystem.cs index d577f8dfc64b..71ca2397d987 100644 --- a/Content.Shared/Gibbing/Systems/GibbingSystem.cs +++ b/Content.Shared/Gibbing/Systems/GibbingSystem.cs @@ -41,17 +41,19 @@ public sealed class GibbingSystem : EntitySystem /// 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(EntityUid outerEntity, EntityUid target, GibType gibType, GibContentsOption gibContentsOption, out HashSet droppedEntities, GibbableComponent? gibable = null, 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) + float randomSpreadMod = 1.0f, bool playAudio = true, List? allowedContainers = null, + List? excludedContainers = null, bool logMissingGibable = false) { droppedEntities = new(); return TryGibEntityWithRef(outerEntity, target, gibType, gibContentsOption, ref droppedEntities, gibable, launchGibs, launchDirection, launchImpulse, launchImpulseVariance, launchCone, randomSpreadMod, playAudio, - allowedContainers, excludedContainers); + allowedContainers, excludedContainers, logMissingGibable); } @@ -74,14 +76,27 @@ public bool TryGibEntity(EntityUid outerEntity, EntityUid target, GibType gibTyp /// 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(EntityUid outerEntity, EntityUid target, GibType gibType, GibContentsOption gibContentsOption, ref HashSet droppedEntities, GibbableComponent? gibable = null, 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) + float randomSpreadMod = 1.0f, bool playAudio = true, List? allowedContainers = null, + List? excludedContainers = null, bool logMissingGibable = false) { - if (!Resolve(target, ref gibable, logMissing: true)) + if (!Resolve(target, ref gibable, logMissing: false)) + { + DropEntity(target, Transform(outerEntity), randomSpreadMod, null, ref droppedEntities, + launchGibs, launchDirection, launchImpulse, launchImpulseVariance, launchCone); + if (logMissingGibable) + { + Log.Warning($"{ToPrettyString(target)} 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) @@ -159,9 +174,12 @@ private void DropEntity(EntityUid target, TransformComponent parentXform, float ref HashSet droppedEntities, bool flingEntity, Vector2? scatterDirection, float scatterImpulse, float scatterImpulseVariance, Angle scatterCone) { - if (!Resolve(target, ref gibable, logMissing: false)) - return; - var gibAttemptEvent = new AttemptEntityGibEvent(target, gibable.GibCount, GibType.Drop); + var gibCount = 0; + if (Resolve(target, ref gibable, logMissing: false)) + { + gibCount = gibable.GibCount; + } + var gibAttemptEvent = new AttemptEntityGibEvent(target, gibCount, GibType.Drop); RaiseLocalEvent(target, ref gibAttemptEvent); switch (gibAttemptEvent.GibType) { @@ -189,9 +207,14 @@ private List GibEntity(EntityUid target, TransformComponent parentXfo float scatterImpulseVariance, Angle scatterCone, bool deleteTarget = true) { var localGibs = new List(); - if (!Resolve(target, ref gibable, logMissing: false)) - return localGibs; - var gibAttemptEvent = new AttemptEntityGibEvent(target, gibable.GibCount, GibType.Drop); + var gibCount = 0; + var gibProtoCount = 0; + if (Resolve(target, ref gibable, logMissing: false)) + { + gibCount = gibable.GibCount; + gibProtoCount = gibable.GibPrototypes.Count; + } + var gibAttemptEvent = new AttemptEntityGibEvent(target, gibCount, GibType.Drop); RaiseLocalEvent(target, ref gibAttemptEvent); switch (gibAttemptEvent.GibType) { @@ -204,7 +227,7 @@ private List GibEntity(EntityUid target, TransformComponent parentXfo return localGibs; } - if (gibable.GibPrototypes.Count > 0) + if (gibable != null && gibProtoCount > 0) { if (flingEntity) { From 31aa5cd017e9ce9d6dce4619001fcd2032bfaa63 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Fri, 9 Feb 2024 23:12:38 -0800 Subject: [PATCH 4/8] Allowing audio params and drop scattering customization per component. Created UnGibbable organ base types and made brains ungibbable. Removed delete brain from gibBody function. Artifact crusher does not destroy brains anymore. It only destroyed brains before not other organs which was wierd. --- Content.Server/Body/Systems/BodySystem.cs | 17 +- .../Systems/ArtifactCrusherSystem.cs | 2 +- .../Body/Systems/SharedBodySystem.Body.cs | 24 +- .../Gibbing/Components/GibbableComponent.cs | 30 ++- .../Gibbing/Systems/GibbingSystem.cs | 209 ++++++++++-------- .../Prototypes/Body/Organs/Animal/animal.yml | 11 +- Resources/Prototypes/Body/Organs/human.yml | 14 +- Resources/Prototypes/Body/Parts/animal.yml | 2 - Resources/Prototypes/Body/Parts/base.yml | 2 - Resources/Prototypes/Body/Parts/skeleton.yml | 2 - .../Prototypes/Body/Parts/terminator.yml | 2 - 11 files changed, 188 insertions(+), 127 deletions(-) diff --git a/Content.Server/Body/Systems/BodySystem.cs b/Content.Server/Body/Systems/BodySystem.cs index 7a1b18f728ec..17971d669924 100644 --- a/Content.Server/Body/Systems/BodySystem.cs +++ b/Content.Server/Body/Systems/BodySystem.cs @@ -107,9 +107,18 @@ protected override void RemovePart( _humanoidSystem.SetLayersVisibility(bodyUid, layers, false, true, humanoid); } - public override HashSet GibBody(EntityUid bodyId, bool gibOrgans = false, BodyComponent? body = null, - bool deleteItems = false, bool deleteBrain = false, GibbableComponent? gibbable = null, SoundSpecifier? gibSound = null, - bool launchGibs = true, Vector2? splatDirection = null, float splatModifier = 1, Angle splatCone = default) + public override HashSet GibBody( + EntityUid bodyId, + bool gibOrgans = false, + BodyComponent? body = null , + bool deleteItems = false, + bool launchGibs = true, + Vector2? splatDirection = null, + float splatModifier = 1, + Angle splatCone = default, + SoundSpecifier? gibSound = null, + AudioParams? gibAudioParams = null + ) { if (!Resolve(bodyId, ref body, false)) return new HashSet(); @@ -121,7 +130,7 @@ public override HashSet GibBody(EntityUid bodyId, bool gibOrgans = fa if (xform.MapUid == null) return new HashSet(); - var gibs = base.GibBody(bodyId, gibOrgans, body, deleteItems, deleteBrain, launchGibs: launchGibs, + var gibs = base.GibBody(bodyId, gibOrgans, body, deleteItems, launchGibs: launchGibs, splatDirection: splatDirection, splatModifier: splatModifier, splatCone:splatCone); RaiseLocalEvent(bodyId, new BeingGibbedEvent(gibs)); QueueDel(bodyId); diff --git a/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactCrusherSystem.cs b/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactCrusherSystem.cs index 09fdc260d7cd..6606f284327e 100644 --- a/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactCrusherSystem.cs +++ b/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactCrusherSystem.cs @@ -106,7 +106,7 @@ public void FinishCrushing(Entity(contained, out var body)) Del(contained); - var gibs = _body.GibBody(contained, body: body, gibOrgans: true, deleteBrain: true); + var gibs = _body.GibBody(contained, body: body, gibOrgans: true); foreach (var gib in gibs) { ContainerSystem.Insert((gib, null, null, null), crusher.OutputContainer); diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs index 5e13f8269420..bf2d17983088 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs @@ -271,10 +271,18 @@ public IEnumerable GetBodyAllSlots(EntityUid bodyId, BodyComponent } } - public virtual HashSet GibBody(EntityUid bodyId, bool gibOrgans = false, - BodyComponent? body = null ,bool deleteItems = false, bool deleteBrain = false, GibbableComponent? gibbable = null, - SoundSpecifier? gibSound = null, bool launchGibs = true, Vector2? splatDirection = null, - float splatModifier = 1, Angle splatCone = default) + public virtual HashSet GibBody( + EntityUid bodyId, + bool gibOrgans = false, + BodyComponent? body = null , + bool deleteItems = false, + bool launchGibs = true, + Vector2? splatDirection = null, + float splatModifier = 1, + Angle splatCone = default, + SoundSpecifier? gibSound = null, + AudioParams? gibAudioParams = null + ) { var gibs = new HashSet(); @@ -282,10 +290,10 @@ public virtual HashSet GibBody(EntityUid bodyId, bool gibOrgans = fal return gibs; var root = GetRootPartOrNull(bodyId, body); - if (root != null && gibSound == null) + if (root != null && TryComp(root.Value.Entity, out GibbableComponent? gibbable)) { - if (Resolve(root.Value.Entity, ref gibbable)) - gibSound = gibbable.GibSound; + gibSound ??= gibbable.GibSound; + gibAudioParams ??= gibbable.GibAudioParams; } var parts = GetBodyChildren(bodyId, body).ToArray(); gibs.EnsureCapacity(parts.Length); @@ -314,7 +322,7 @@ public virtual HashSet GibBody(EntityUid bodyId, bool gibOrgans = fal gibs.Add(item); } } - _audioSystem.PlayPredicted(gibSound, Transform(bodyId).Coordinates, null, GibbableComponent.GibAudioParams); + _audioSystem.PlayPredicted(gibSound, Transform(bodyId).Coordinates, null, gibAudioParams); return gibs; } } diff --git a/Content.Shared/Gibbing/Components/GibbableComponent.cs b/Content.Shared/Gibbing/Components/GibbableComponent.cs index d023082c6e1a..e13856b1e538 100644 --- a/Content.Shared/Gibbing/Components/GibbableComponent.cs +++ b/Content.Shared/Gibbing/Components/GibbableComponent.cs @@ -8,17 +8,33 @@ namespace Content.Shared.Gibbing.Components; [RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(GibbingSystem))] public sealed partial class GibbableComponent : Component { - [DataField(required:true), AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)] + /// + /// Giblet entity prototypes to randomly select from when spawning additional giblets + /// + [DataField, AutoNetworkedField] public List GibPrototypes = new(); - [DataField(required:true), AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)] - public int GibCount = 3; + /// + /// Number of giblet entities to spawn in addition to entity contents + /// + [DataField, AutoNetworkedField] + public int GibCount; - [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)] + /// + /// 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"); - public const float GibScatterRange = 0.3f; - - public static readonly AudioParams GibAudioParams = 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; + /// + /// Audio parameters for when the gibbing sound is played + /// + [DataField, AutoNetworkedField] + public AudioParams GibAudioParams = AudioParams.Default.WithVariation(0.025f); } diff --git a/Content.Shared/Gibbing/Systems/GibbingSystem.cs b/Content.Shared/Gibbing/Systems/GibbingSystem.cs index 71ca2397d987..189d5ed6e7d2 100644 --- a/Content.Shared/Gibbing/Systems/GibbingSystem.cs +++ b/Content.Shared/Gibbing/Systems/GibbingSystem.cs @@ -22,17 +22,15 @@ public sealed class GibbingSystem : EntitySystem //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 we wish to gib + /// 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 - /// The gibable component /// 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 @@ -44,14 +42,16 @@ public sealed class GibbingSystem : EntitySystem /// /// 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(EntityUid outerEntity, EntityUid target, GibType gibType, GibContentsOption gibContentsOption, - out HashSet droppedEntities, GibbableComponent? gibable = null, bool launchGibs = true, - Vector2 launchDirection = default, float launchImpulse = 0f, float launchImpulseVariance = 0f, Angle launchCone = default, + public bool TryGibEntity(EntityUid 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, target, gibType, gibContentsOption, ref droppedEntities, gibable, + return TryGibEntityWithRef(outerEntity, gibbable, gibType, gibContentsOption, ref droppedEntities, launchGibs, launchDirection, launchImpulse, launchImpulseVariance, launchCone, randomSpreadMod, playAudio, allowedContainers, excludedContainers, logMissingGibable); } @@ -62,11 +62,10 @@ public bool TryGibEntity(EntityUid outerEntity, EntityUid target, GibType gibTyp /// 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 we wish to gib + /// 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 - /// The gibable component /// 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 @@ -78,19 +77,30 @@ public bool TryGibEntity(EntityUid outerEntity, EntityUid target, GibType gibTyp /// 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(EntityUid outerEntity, EntityUid target, GibType gibType, GibContentsOption gibContentsOption, - ref HashSet droppedEntities, GibbableComponent? gibable = null, 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) + public bool TryGibEntityWithRef( + EntityUid 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(target, ref gibable, logMissing: false)) + if (!Resolve(gibbable, ref gibbable.Comp, logMissing: false)) { - DropEntity(target, Transform(outerEntity), randomSpreadMod, null, ref droppedEntities, + DropEntity(gibbable, Transform(outerEntity), randomSpreadMod, ref droppedEntities, launchGibs, launchDirection, launchImpulse, launchImpulseVariance, launchCone); if (logMissingGibable) { - Log.Warning($"{ToPrettyString(target)} does not have a GibbableComponent! " + + Log.Warning($"{ToPrettyString(gibbable)} does not have a GibbableComponent! " + $"This is not required but may cause issues contained items to not be dropped."); } @@ -103,46 +113,52 @@ public bool TryGibEntityWithRef(EntityUid outerEntity, EntityUid target, GibType { randomSpreadMod = 0; } + var parentXform = Transform(outerEntity); HashSet validContainers = new(); - foreach (var container in _containerSystem.GetAllContainers(target)) + foreach (var container in _containerSystem.GetAllContainers(gibbable)) { var valid = true; if (allowedContainers != null) - valid = allowedContainers.Contains(container.ID); + valid = allowedContainers.Contains(container.ID); if (excludedContainers != null) - valid = valid && !excludedContainers.Contains(container.ID); + valid = valid && !excludedContainers.Contains(container.ID); if (valid) validContainers.Add(container); } + switch (gibContentsOption) { - case GibContentsOption.Skip: - break; - case GibContentsOption.Drop: + case GibContentsOption.Skip: + break; + case GibContentsOption.Drop: + { + foreach (var container in validContainers) { - foreach (var container in validContainers) + foreach (var ent in container.ContainedEntities) { - foreach (var ent in container.ContainedEntities) - { - DropEntity(ent, parentXform, randomSpreadMod, gibable, ref droppedEntities, launchGibs, - launchDirection, launchImpulse, launchImpulseVariance, launchCone); - } + DropEntity(new Entity(ent, null), parentXform, randomSpreadMod, + ref droppedEntities, launchGibs, + launchDirection, launchImpulse, launchImpulseVariance, launchCone); } - break; } - case GibContentsOption.Gib: + + break; + } + case GibContentsOption.Gib: + { + foreach (var container in _containerSystem.GetAllContainers(gibbable)) { - foreach (var container in _containerSystem.GetAllContainers(target)) + foreach (var ent in container.ContainedEntities) { - foreach (var ent in container.ContainedEntities) - { - GibEntity(ent, parentXform, randomSpreadMod, gibable,ref droppedEntities, launchGibs, - launchDirection, launchImpulse, launchImpulseVariance, launchCone); - } + GibEntity(new Entity(ent, null), parentXform, randomSpreadMod, + ref droppedEntities, launchGibs, + launchDirection, launchImpulse, launchImpulseVariance, launchCone); } - break; } + + break; + } } switch (gibType) @@ -151,134 +167,145 @@ public bool TryGibEntityWithRef(EntityUid outerEntity, EntityUid target, GibType break; case GibType.Drop: { - DropEntity(target, parentXform, randomSpreadMod, gibable, ref droppedEntities, launchGibs, + DropEntity(gibbable, parentXform, randomSpreadMod, ref droppedEntities, launchGibs, launchDirection, launchImpulse, launchImpulseVariance, launchCone); break; } case GibType.Gib: { - GibEntity(target, parentXform, randomSpreadMod, gibable, ref droppedEntities, launchGibs, + GibEntity(gibbable, parentXform, randomSpreadMod, ref droppedEntities, launchGibs, launchDirection, launchImpulse, launchImpulseVariance, launchCone); break; } } + if (playAudio) - _audioSystem.PlayPredicted(gibable.GibSound, parentXform.Coordinates, null, GibbableComponent.GibAudioParams); + { + _audioSystem.PlayPredicted(gibbable.Comp.GibSound, parentXform.Coordinates, null, + gibbable.Comp.GibAudioParams); + } if (gibType == GibType.Gib) - EntityManager.QueueDeleteEntity(target); + QueueDel(gibbable); return true; } - private void DropEntity(EntityUid target, TransformComponent parentXform, float randomSpreadMod, GibbableComponent? gibable, + private void DropEntity(Entity gibbable, TransformComponent parentXform, float randomSpreadMod, ref HashSet droppedEntities, bool flingEntity, Vector2? scatterDirection, float scatterImpulse, float scatterImpulseVariance, Angle scatterCone) { var gibCount = 0; - if (Resolve(target, ref gibable, logMissing: false)) + if (Resolve(gibbable, ref gibbable.Comp, logMissing: false)) { - gibCount = gibable.GibCount; + gibCount = gibbable.Comp.GibCount; } - var gibAttemptEvent = new AttemptEntityGibEvent(target, gibCount, GibType.Drop); - RaiseLocalEvent(target, ref gibAttemptEvent); + + var gibAttemptEvent = new AttemptEntityGibEvent(gibbable, gibCount, GibType.Drop); + RaiseLocalEvent(gibbable, ref gibAttemptEvent); switch (gibAttemptEvent.GibType) { case GibType.Skip: return; case GibType.Gib: - GibEntity(target, parentXform, randomSpreadMod, gibable, ref droppedEntities, flingEntity,scatterDirection, - scatterImpulse, scatterImpulseVariance, scatterCone ,deleteTarget: false); + GibEntity(gibbable, parentXform, randomSpreadMod, ref droppedEntities, flingEntity, scatterDirection, + scatterImpulse, scatterImpulseVariance, scatterCone, deleteTarget: false); return; } - _transformSystem.AttachToGridOrMap(target); - _transformSystem.SetCoordinates(target,parentXform.Coordinates); - _transformSystem.SetWorldRotation(target, _random.NextAngle()); - droppedEntities.Add(target); + + _transformSystem.AttachToGridOrMap(gibbable); + _transformSystem.SetCoordinates(gibbable, parentXform.Coordinates); + _transformSystem.SetWorldRotation(gibbable, _random.NextAngle()); + droppedEntities.Add(gibbable); if (flingEntity) { - FlingDroppedEntity(target, scatterDirection, scatterImpulse, scatterImpulseVariance, scatterCone); + FlingDroppedEntity(gibbable, scatterDirection, scatterImpulse, scatterImpulseVariance, scatterCone); } - var gibbedEvent = new EntityGibbedEvent(target, new List{target}); - RaiseLocalEvent(target,ref gibbedEvent); + + var gibbedEvent = new EntityGibbedEvent(gibbable, new List {gibbable}); + RaiseLocalEvent(gibbable, ref gibbedEvent); } - private List GibEntity(EntityUid target, TransformComponent parentXform, float randomSpreadMod, - GibbableComponent? gibable, ref HashSet droppedEntities, bool flingEntity, Vector2? scatterDirection, float scatterImpulse, + private List GibEntity(Entity gibbable, TransformComponent parentXform, + 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(target, ref gibable, logMissing: false)) + if (Resolve(gibbable, ref gibbable.Comp, logMissing: false)) { - gibCount = gibable.GibCount; - gibProtoCount = gibable.GibPrototypes.Count; + gibCount = gibbable.Comp.GibCount; + gibProtoCount = gibbable.Comp.GibPrototypes.Count; } - var gibAttemptEvent = new AttemptEntityGibEvent(target, gibCount, GibType.Drop); - RaiseLocalEvent(target, ref gibAttemptEvent); + + var gibAttemptEvent = new AttemptEntityGibEvent(gibbable, gibCount, GibType.Drop); + RaiseLocalEvent(gibbable, ref gibAttemptEvent); switch (gibAttemptEvent.GibType) { case GibType.Skip: return localGibs; case GibType.Drop: - DropEntity(target, parentXform, randomSpreadMod, gibable, ref droppedEntities, flingEntity, + DropEntity(gibbable, parentXform, randomSpreadMod, ref droppedEntities, flingEntity, scatterDirection, scatterImpulse, scatterImpulseVariance, scatterCone); - localGibs.Add(target); + localGibs.Add(gibbable); return localGibs; } - if (gibable != null && gibProtoCount > 0) + if (gibbable.Comp != null && gibProtoCount > 0) { if (flingEntity) { for (var i = 0; i < gibAttemptEvent.GibletCount; i++) { - if (!TryCreateRandomGiblet(gibable, parentXform.Coordinates, false, out var giblet, + if (!TryCreateRandomGiblet(gibbable.Comp, parentXform.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(gibable, parentXform.Coordinates, false, out var giblet, randomSpreadMod)) + if (TryCreateRandomGiblet(gibbable.Comp, parentXform.Coordinates, false, out var giblet, + randomSpreadMod)) droppedEntities.Add(giblet.Value); } } - } - _transformSystem.AttachToGridOrMap(target, Transform(target)); + + _transformSystem.AttachToGridOrMap(gibbable, Transform(gibbable)); if (flingEntity) { - FlingDroppedEntity(target, scatterDirection, scatterImpulse, scatterImpulseVariance, scatterCone); + FlingDroppedEntity(gibbable, scatterDirection, scatterImpulse, scatterImpulseVariance, scatterCone); } - var gibbedEvent = new EntityGibbedEvent(target, localGibs); - RaiseLocalEvent(target,ref gibbedEvent); + + var gibbedEvent = new EntityGibbedEvent(gibbable, localGibs); + RaiseLocalEvent(gibbable, ref gibbedEvent); if (deleteTarget) - EntityManager.QueueDeleteEntity(target); + QueueDel(gibbable); return localGibs; } - public bool TryCreateRandomGiblet(EntityUid target, [NotNullWhen(true)] out EntityUid? gibletEntity , - GibbableComponent? gibable = null, float randomSpreadModifier = 1.0f, bool playSound = true) + public bool TryCreateRandomGiblet(Entity gibbable, [NotNullWhen(true)] out EntityUid? gibletEntity, + float randomSpreadModifier = 1.0f, bool playSound = true) { gibletEntity = null; - return Resolve(target, ref gibable) && TryCreateRandomGiblet(gibable, Transform(target).Coordinates, - playSound ,out gibletEntity, randomSpreadModifier); + return Resolve(gibbable, ref gibbable.Comp) && TryCreateRandomGiblet(gibbable.Comp, Transform(gibbable).Coordinates, + playSound, out gibletEntity, randomSpreadModifier); } - public bool TryCreateAndFlingRandomGiblet(EntityUid target, [NotNullWhen(true)] out EntityUid? gibletEntity , - Vector2 scatterDirection, float force, float scatterImpulseVariance, Angle scatterCone = default, GibbableComponent? gibable = null, bool playSound = true) + 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(target, ref gibable) || - !TryCreateRandomGiblet(gibable, Transform(target).Coordinates, playSound, out gibletEntity)) + 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; @@ -288,21 +315,23 @@ private void FlingDroppedEntity(EntityUid target, Vector2? direction, float impu Angle scatterConeAngle) { var scatterAngle = direction?.ToAngle() ?? _random.NextAngle(); - var scatterVector = _random.NextAngle(scatterAngle - scatterConeAngle/2,scatterAngle + scatterConeAngle/2) - .ToVec()*(impulse+_random.NextFloat(impulseVariance)); + var scatterVector = _random.NextAngle(scatterAngle - scatterConeAngle / 2, scatterAngle + scatterConeAngle / 2) + .ToVec() * (impulse + _random.NextFloat(impulseVariance)); _physicsSystem.ApplyLinearImpulse(target, scatterVector); } - private bool TryCreateRandomGiblet(GibbableComponent gibable, EntityCoordinates coords, + private bool TryCreateRandomGiblet(GibbableComponent gibbable, EntityCoordinates coords, bool playSound, [NotNullWhen(true)] out EntityUid? gibletEntity, float? randomSpreadModifier = null) { gibletEntity = null; - if (gibable.GibPrototypes.Count == 0) + if (gibbable.GibPrototypes.Count == 0) return false; - gibletEntity = EntityManager.SpawnEntity(gibable.GibPrototypes[_random.Next(0, gibable.GibPrototypes.Count)], - randomSpreadModifier == null ? coords : coords.Offset(_random.NextVector2(GibbableComponent.GibScatterRange * randomSpreadModifier.Value))); + 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(gibable.GibSound, coords, null, GibbableComponent.GibAudioParams); + _audioSystem.PlayPredicted(gibbable.GibSound, coords, null, gibbable.GibAudioParams); _transformSystem.SetWorldRotation(gibletEntity.Value, _random.NextAngle()); return true; } diff --git a/Resources/Prototypes/Body/Organs/Animal/animal.yml b/Resources/Prototypes/Body/Organs/Animal/animal.yml index 9051ffbaf8b7..2f50821df353 100644 --- a/Resources/Prototypes/Body/Organs/Animal/animal.yml +++ b/Resources/Prototypes/Body/Organs/Animal/animal.yml @@ -1,5 +1,5 @@ - type: entity - id: BaseAnimalOrgan + id: BaseAnimalOrganUnGibbable parent: BaseItem abstract: true components: @@ -9,9 +9,6 @@ sprite: Mobs/Species/Human/organs.rsi - type: StaticPrice price: 50 - - type: Gibbable - gibPrototypes: [] - gibCount: 3 - type: SolutionContainerManager solutions: food: @@ -26,6 +23,12 @@ tags: - Meat +- type: entity + id: BaseAnimalOrgan + parent: BaseAnimalOrganUnGibbable + abstract: true + components: + - type: Gibbable - type: entity id: OrganAnimalLungs diff --git a/Resources/Prototypes/Body/Organs/human.yml b/Resources/Prototypes/Body/Organs/human.yml index a41536ca1cc2..fe33c11029f0 100644 --- a/Resources/Prototypes/Body/Organs/human.yml +++ b/Resources/Prototypes/Body/Organs/human.yml @@ -1,5 +1,5 @@ - type: entity - id: BaseHumanOrgan + id: BaseHumanOrganUnGibbable parent: BaseItem abstract: true components: @@ -7,9 +7,6 @@ sprite: Mobs/Species/Human/organs.rsi - type: Organ - type: Food - - type: Gibbable - gibPrototypes: [] - gibCount: 3 - type: Extractable grindableSolutionName: organ - type: SolutionContainerManager @@ -30,9 +27,16 @@ tags: - Meat +- type: entity + id: BaseHumanOrgan + parent: BaseHumanOrganUnGibbable + abstract: true + components: + - type: Gibbable + - type: entity id: OrganHumanBrain - parent: BaseHumanOrgan + parent: BaseHumanOrganUnGibbable 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 185f208f15dd..4db026b40fbc 100644 --- a/Resources/Prototypes/Body/Parts/animal.yml +++ b/Resources/Prototypes/Body/Parts/animal.yml @@ -24,8 +24,6 @@ tags: - Trash - type: Gibbable - gibPrototypes: [ ] - gibCount: 3 - type: Extractable juiceSolution: reagents: diff --git a/Resources/Prototypes/Body/Parts/base.yml b/Resources/Prototypes/Body/Parts/base.yml index b5c87de96605..836d0f140afe 100644 --- a/Resources/Prototypes/Body/Parts/base.yml +++ b/Resources/Prototypes/Body/Parts/base.yml @@ -10,8 +10,6 @@ damageContainer: Biological - type: BodyPart - type: Gibbable - gibPrototypes: [] - gibCount: 3 - type: ContainerContainer containers: bodypart: !type:Container diff --git a/Resources/Prototypes/Body/Parts/skeleton.yml b/Resources/Prototypes/Body/Parts/skeleton.yml index 29400da5e918..1b378c62332f 100644 --- a/Resources/Prototypes/Body/Parts/skeleton.yml +++ b/Resources/Prototypes/Body/Parts/skeleton.yml @@ -15,8 +15,6 @@ - type: StaticPrice price: 20 - type: Gibbable - gibPrototypes: [ ] - gibCount: 3 - type: Tag tags: - Trash diff --git a/Resources/Prototypes/Body/Parts/terminator.yml b/Resources/Prototypes/Body/Parts/terminator.yml index 379c6f9293f3..58530da959c6 100644 --- a/Resources/Prototypes/Body/Parts/terminator.yml +++ b/Resources/Prototypes/Body/Parts/terminator.yml @@ -17,8 +17,6 @@ bodypart: !type:Container ents: [] - type: Gibbable - gibPrototypes: [ ] - gibCount: 3 - type: StaticPrice price: 200 From b6ef619ffab51af55036f5d8ff3fee500e6f1a99 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 10 Feb 2024 02:07:03 -0800 Subject: [PATCH 5/8] Update Content.Shared/Body/Systems/SharedBodySystem.Body.cs Fixing space for multiplication Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> --- Content.Shared/Body/Systems/SharedBodySystem.Body.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs index bf2d17983088..3bda498c7981 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs @@ -310,7 +310,7 @@ public virtual HashSet GibBody( 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, + ref gibs, playAudio: false, launchImpulse: GibletLaunchImpulse * splatModifier, launchImpulseVariance:GibletLaunchImpulseVariance, launchCone: splatCone); } } From 648d7c7c921b7208ef8fe273443e63bac9c7e7f7 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 10 Feb 2024 15:13:30 -0800 Subject: [PATCH 6/8] Added event raised when attempting to gib contained entities to allow modification of allowed and excluded container ids --- Content.Shared/Gibbing/Events/GibbingEvents.cs | 17 +++++++++++++++++ Content.Shared/Gibbing/Systems/GibbingSystem.cs | 4 ++++ 2 files changed, 21 insertions(+) diff --git a/Content.Shared/Gibbing/Events/GibbingEvents.cs b/Content.Shared/Gibbing/Events/GibbingEvents.cs index a24298823b5b..949b10eab9a8 100644 --- a/Content.Shared/Gibbing/Events/GibbingEvents.cs +++ b/Content.Shared/Gibbing/Events/GibbingEvents.cs @@ -2,6 +2,23 @@ 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 /// diff --git a/Content.Shared/Gibbing/Systems/GibbingSystem.cs b/Content.Shared/Gibbing/Systems/GibbingSystem.cs index 189d5ed6e7d2..b0e581138668 100644 --- a/Content.Shared/Gibbing/Systems/GibbingSystem.cs +++ b/Content.Shared/Gibbing/Systems/GibbingSystem.cs @@ -116,6 +116,10 @@ public bool TryGibEntityWithRef( var parentXform = Transform(outerEntity); 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; From d45fe7adea9009306ae6c1d9114b563663a88d98 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 10 Feb 2024 15:25:49 -0800 Subject: [PATCH 7/8] removing audioParams var from component (sound specifier includes it) --- Content.Shared/Body/Systems/SharedBodySystem.Body.cs | 3 +-- Content.Shared/Gibbing/Components/GibbableComponent.cs | 8 +------- Content.Shared/Gibbing/Systems/GibbingSystem.cs | 5 ++--- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs index 3bda498c7981..a76c9fb495e9 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs @@ -293,7 +293,6 @@ public virtual HashSet GibBody( if (root != null && TryComp(root.Value.Entity, out GibbableComponent? gibbable)) { gibSound ??= gibbable.GibSound; - gibAudioParams ??= gibbable.GibAudioParams; } var parts = GetBodyChildren(bodyId, body).ToArray(); gibs.EnsureCapacity(parts.Length); @@ -322,7 +321,7 @@ public virtual HashSet GibBody( gibs.Add(item); } } - _audioSystem.PlayPredicted(gibSound, Transform(bodyId).Coordinates, null, gibAudioParams); + _audioSystem.PlayPredicted(gibSound, Transform(bodyId).Coordinates, null); return gibs; } } diff --git a/Content.Shared/Gibbing/Components/GibbableComponent.cs b/Content.Shared/Gibbing/Components/GibbableComponent.cs index e13856b1e538..9c7501f0282f 100644 --- a/Content.Shared/Gibbing/Components/GibbableComponent.cs +++ b/Content.Shared/Gibbing/Components/GibbableComponent.cs @@ -24,17 +24,11 @@ public sealed partial class GibbableComponent : Component /// 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"); + 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; - - /// - /// Audio parameters for when the gibbing sound is played - /// - [DataField, AutoNetworkedField] - public AudioParams GibAudioParams = AudioParams.Default.WithVariation(0.025f); } diff --git a/Content.Shared/Gibbing/Systems/GibbingSystem.cs b/Content.Shared/Gibbing/Systems/GibbingSystem.cs index b0e581138668..5d00b2a49128 100644 --- a/Content.Shared/Gibbing/Systems/GibbingSystem.cs +++ b/Content.Shared/Gibbing/Systems/GibbingSystem.cs @@ -185,8 +185,7 @@ public bool TryGibEntityWithRef( if (playAudio) { - _audioSystem.PlayPredicted(gibbable.Comp.GibSound, parentXform.Coordinates, null, - gibbable.Comp.GibAudioParams); + _audioSystem.PlayPredicted(gibbable.Comp.GibSound, parentXform.Coordinates, null); } if (gibType == GibType.Gib) @@ -335,7 +334,7 @@ private bool TryCreateRandomGiblet(GibbableComponent gibbable, EntityCoordinates ? coords : coords.Offset(_random.NextVector2(gibbable.GibScatterRange * randomSpreadModifier.Value))); if (playSound) - _audioSystem.PlayPredicted(gibbable.GibSound, coords, null, gibbable.GibAudioParams); + _audioSystem.PlayPredicted(gibbable.GibSound, coords, null); _transformSystem.SetWorldRotation(gibletEntity.Value, _random.NextAngle()); return true; } From adea949f0997880fa61b6a6c7bdf997c2f4d6447 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 10 Feb 2024 15:28:23 -0800 Subject: [PATCH 8/8] Fixing signature --- Content.Server/Body/Systems/BodySystem.cs | 3 +-- Content.Shared/Body/Systems/SharedBodySystem.Body.cs | 7 +++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Content.Server/Body/Systems/BodySystem.cs b/Content.Server/Body/Systems/BodySystem.cs index 17971d669924..0e9b295f718d 100644 --- a/Content.Server/Body/Systems/BodySystem.cs +++ b/Content.Server/Body/Systems/BodySystem.cs @@ -116,8 +116,7 @@ public override HashSet GibBody( Vector2? splatDirection = null, float splatModifier = 1, Angle splatCone = default, - SoundSpecifier? gibSound = null, - AudioParams? gibAudioParams = null + SoundSpecifier? gibSoundOverride = null ) { if (!Resolve(bodyId, ref body, false)) diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs index a76c9fb495e9..85b6758d78a1 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs @@ -280,8 +280,7 @@ public virtual HashSet GibBody( Vector2? splatDirection = null, float splatModifier = 1, Angle splatCone = default, - SoundSpecifier? gibSound = null, - AudioParams? gibAudioParams = null + SoundSpecifier? gibSoundOverride = null ) { var gibs = new HashSet(); @@ -292,7 +291,7 @@ public virtual HashSet GibBody( var root = GetRootPartOrNull(bodyId, body); if (root != null && TryComp(root.Value.Entity, out GibbableComponent? gibbable)) { - gibSound ??= gibbable.GibSound; + gibSoundOverride ??= gibbable.GibSound; } var parts = GetBodyChildren(bodyId, body).ToArray(); gibs.EnsureCapacity(parts.Length); @@ -321,7 +320,7 @@ public virtual HashSet GibBody( gibs.Add(item); } } - _audioSystem.PlayPredicted(gibSound, Transform(bodyId).Coordinates, null); + _audioSystem.PlayPredicted(gibSoundOverride, Transform(bodyId).Coordinates, null); return gibs; } }