diff --git a/Content.Client/GameObjects/EntitySystems/MeleeWeaponSystem.cs b/Content.Client/GameObjects/EntitySystems/MeleeWeaponSystem.cs index 7f4ec946fc..e35c150cd2 100644 --- a/Content.Client/GameObjects/EntitySystems/MeleeWeaponSystem.cs +++ b/Content.Client/GameObjects/EntitySystems/MeleeWeaponSystem.cs @@ -51,39 +51,42 @@ namespace Content.Client.GameObjects.EntitySystems var attacker = EntityManager.GetEntity(msg.Attacker); - var lunge = attacker.EnsureComponent(); - lunge.SetData(msg.Angle); - - var entity = EntityManager.SpawnEntity(weaponArc.Prototype, attacker.Transform.Coordinates); - entity.Transform.LocalRotation = msg.Angle; - - var weaponArcAnimation = entity.GetComponent(); - weaponArcAnimation.SetData(weaponArc, msg.Angle, attacker, msg.ArcFollowAttacker); - - // Due to ISpriteComponent limitations, weapons that don't use an RSI won't have this effect. - if (EntityManager.TryGetEntity(msg.Source, out var source) && msg.TextureEffect && source.TryGetComponent(out ISpriteComponent sourceSprite) - && sourceSprite.BaseRSI?.Path != null) + if (!attacker.Deleted) { - var sys = Get(); - var curTime = _gameTiming.CurTime; - var effect = new EffectSystemMessage + var lunge = attacker.EnsureComponent(); + lunge.SetData(msg.Angle); + + var entity = EntityManager.SpawnEntity(weaponArc.Prototype, attacker.Transform.Coordinates); + entity.Transform.LocalRotation = msg.Angle; + + var weaponArcAnimation = entity.GetComponent(); + weaponArcAnimation.SetData(weaponArc, msg.Angle, attacker, msg.ArcFollowAttacker); + + // Due to ISpriteComponent limitations, weapons that don't use an RSI won't have this effect. + if (EntityManager.TryGetEntity(msg.Source, out var source) && msg.TextureEffect && source.TryGetComponent(out ISpriteComponent sourceSprite) + && sourceSprite.BaseRSI?.Path != null) { - EffectSprite = sourceSprite.BaseRSI.Path.ToString(), - RsiState = sourceSprite.LayerGetState(0).Name, - Coordinates = attacker.Transform.Coordinates, - Color = Vector4.Multiply(new Vector4(255, 255, 255, 125), 1.0f), - ColorDelta = Vector4.Multiply(new Vector4(0, 0, 0, -10), 1.0f), - Velocity = msg.Angle.ToVec(), - Acceleration = msg.Angle.ToVec() * 5f, - Born = curTime, - DeathTime = curTime.Add(TimeSpan.FromMilliseconds(300f)), - }; - sys.CreateEffect(effect); + var sys = Get(); + var curTime = _gameTiming.CurTime; + var effect = new EffectSystemMessage + { + EffectSprite = sourceSprite.BaseRSI.Path.ToString(), + RsiState = sourceSprite.LayerGetState(0).Name, + Coordinates = attacker.Transform.Coordinates, + Color = Vector4.Multiply(new Vector4(255, 255, 255, 125), 1.0f), + ColorDelta = Vector4.Multiply(new Vector4(0, 0, 0, -10), 1.0f), + Velocity = msg.Angle.ToVec(), + Acceleration = msg.Angle.ToVec() * 5f, + Born = curTime, + DeathTime = curTime.Add(TimeSpan.FromMilliseconds(300f)), + }; + sys.CreateEffect(effect); + } } foreach (var uid in msg.Hits) { - if (!EntityManager.TryGetEntity(uid, out var hitEntity)) + if (!EntityManager.TryGetEntity(uid, out var hitEntity) || hitEntity.Deleted) { continue; } diff --git a/Content.Server/GameObjects/Components/Body/BodyComponent.cs b/Content.Server/GameObjects/Components/Body/BodyComponent.cs index 71637b13ea..000832af1d 100644 --- a/Content.Server/GameObjects/Components/Body/BodyComponent.cs +++ b/Content.Server/GameObjects/Components/Body/BodyComponent.cs @@ -1,15 +1,20 @@ #nullable enable using System; using Content.Server.Commands.Observer; +using Content.Shared.Audio; using Content.Shared.GameObjects.Components.Body; using Content.Shared.GameObjects.Components.Body.Part; using Content.Shared.GameObjects.Components.Damage; using Content.Shared.GameObjects.Components.Mobs.State; using Content.Shared.GameObjects.Components.Movement; +using Content.Shared.Utility; using Robust.Server.GameObjects.Components.Container; +using Robust.Server.GameObjects.EntitySystems; using Robust.Server.Interfaces.Console; using Robust.Server.Interfaces.Player; +using Robust.Shared.Audio; using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Systems; using Robust.Shared.IoC; using Robust.Shared.Log; using Robust.Shared.Players; @@ -41,6 +46,7 @@ namespace Content.Server.GameObjects.Components.Body base.OnRemovePart(slot, part); _partContainer.ForceRemove(part.Owner); + part.Owner.RandomOffset(0.25f); } public override void Initialize() @@ -88,5 +94,29 @@ namespace Content.Server.GameObjects.Components.Body new Ghost().Execute(shell, (IPlayerSession) session, Array.Empty()); } } + + public override void Gib(bool gibParts = false) + { + base.Gib(gibParts); + + EntitySystem.Get() + .PlayAtCoords(AudioHelpers.GetRandomFileFromSoundCollection("gib"), Owner.Transform.Coordinates, + AudioHelpers.WithVariation(0.025f)); + + if (Owner.TryGetComponent(out ContainerManagerComponent? container)) + { + foreach (var cont in container.GetAllContainers()) + { + foreach (var ent in cont.ContainedEntities) + { + cont.ForceRemove(ent); + ent.Transform.Coordinates = Owner.Transform.Coordinates; + ent.RandomOffset(0.25f); + } + } + } + + Owner.Delete(); + } } } diff --git a/Content.Server/GameObjects/Components/Body/Part/BodyPartComponent.cs b/Content.Server/GameObjects/Components/Body/Part/BodyPartComponent.cs index 450d400c40..44c7cc5a7e 100644 --- a/Content.Server/GameObjects/Components/Body/Part/BodyPartComponent.cs +++ b/Content.Server/GameObjects/Components/Body/Part/BodyPartComponent.cs @@ -11,6 +11,7 @@ using Content.Shared.GameObjects.Components.Body.Surgery; using Content.Shared.GameObjects.Verbs; using Content.Shared.Interfaces; using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Utility; using Robust.Server.Console; using Robust.Server.GameObjects; using Robust.Server.GameObjects.Components.Container; @@ -57,6 +58,7 @@ namespace Content.Server.GameObjects.Components.Body.Part base.OnRemoveMechanism(mechanism); _mechanismContainer.Remove(mechanism.Owner); + mechanism.Owner.RandomOffset(0.25f); } public override void Initialize() diff --git a/Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/GibBehavior.cs b/Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/GibBehavior.cs new file mode 100644 index 0000000000..c388666c24 --- /dev/null +++ b/Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/GibBehavior.cs @@ -0,0 +1,27 @@ +using Content.Server.GameObjects.EntitySystems; +using Content.Shared.GameObjects.Components.Body; +using JetBrains.Annotations; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Serialization; + +namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior +{ + [UsedImplicitly] + public class GibBehavior : IThresholdBehavior + { + private bool _recursive = true; + + public void ExposeData(ObjectSerializer serializer) + { + serializer.DataField(ref _recursive, "recursive", true); + } + + public void Trigger(IEntity owner, DestructibleSystem system) + { + if (owner.TryGetComponent(out IBody body)) + { + body.Gib(_recursive); + } + } + } +} diff --git a/Content.Server/GameObjects/Components/Recycling/RecyclerComponent.cs b/Content.Server/GameObjects/Components/Recycling/RecyclerComponent.cs index e5f6943739..a9074492d5 100644 --- a/Content.Server/GameObjects/Components/Recycling/RecyclerComponent.cs +++ b/Content.Server/GameObjects/Components/Recycling/RecyclerComponent.cs @@ -79,7 +79,7 @@ namespace Content.Server.GameObjects.Components.Recycling // Mobs are a special case! if (CanGib(entity)) { - entity.Delete(); // TODO: Gib + entity.GetComponent().Gib(true); Bloodstain(); return; } diff --git a/Content.Shared/GameObjects/Components/Body/IBody.cs b/Content.Shared/GameObjects/Components/Body/IBody.cs index 672ef1c73a..152deb3bb0 100644 --- a/Content.Shared/GameObjects/Components/Body/IBody.cs +++ b/Content.Shared/GameObjects/Components/Body/IBody.cs @@ -247,5 +247,10 @@ namespace Content.Shared.GameObjects.Components.Body /// The index to look in. /// A pair of the part name and body part occupying it. KeyValuePair PartAt(int index); + + /// + /// Gibs this body. + /// + void Gib(bool gibParts = false); } } diff --git a/Content.Shared/GameObjects/Components/Body/Part/IBodyPart.cs b/Content.Shared/GameObjects/Components/Body/Part/IBodyPart.cs index c04dcf8f19..ba8ed2d192 100644 --- a/Content.Shared/GameObjects/Components/Body/Part/IBodyPart.cs +++ b/Content.Shared/GameObjects/Components/Body/Part/IBodyPart.cs @@ -116,5 +116,10 @@ namespace Content.Shared.GameObjects.Components.Body.Part /// false otherwise. /// bool DeleteMechanism(IMechanism mechanism); + + /// + /// Gibs the body part. + /// + void Gib(); } } diff --git a/Content.Shared/GameObjects/Components/Body/Part/SharedBodyPartComponent.cs b/Content.Shared/GameObjects/Components/Body/Part/SharedBodyPartComponent.cs index 97d05c161f..ffbd3a0c70 100644 --- a/Content.Shared/GameObjects/Components/Body/Part/SharedBodyPartComponent.cs +++ b/Content.Shared/GameObjects/Components/Body/Part/SharedBodyPartComponent.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using Content.Shared.GameObjects.Components.Body.Mechanism; using Content.Shared.GameObjects.Components.Body.Surgery; +using Content.Shared.Utility; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.IoC; @@ -313,6 +314,14 @@ namespace Content.Shared.GameObjects.Components.Body.Part protected virtual void OnAddedToBody(IBody body) { } protected virtual void OnRemovedFromBody(IBody old) { } + + public virtual void Gib() + { + foreach (var mechanism in _mechanisms) + { + RemoveMechanism(mechanism); + } + } } [Serializable, NetSerializable] diff --git a/Content.Shared/GameObjects/Components/Body/SharedBodyComponent.cs b/Content.Shared/GameObjects/Components/Body/SharedBodyComponent.cs index 33dda03b24..0c18eaa0df 100644 --- a/Content.Shared/GameObjects/Components/Body/SharedBodyComponent.cs +++ b/Content.Shared/GameObjects/Components/Body/SharedBodyComponent.cs @@ -1,6 +1,7 @@ #nullable enable using System; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Shared.Damage; @@ -11,7 +12,10 @@ using Content.Shared.GameObjects.Components.Body.Template; using Content.Shared.GameObjects.Components.Damage; using Content.Shared.GameObjects.Components.Movement; using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Utility; +using Robust.Shared.Containers; using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components.Containers; using Robust.Shared.GameObjects.Systems; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.IoC; @@ -19,6 +23,7 @@ using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Utility; using Robust.Shared.ViewVariables; +using Component = Robust.Shared.GameObjects.Component; namespace Content.Shared.GameObjects.Components.Body { @@ -697,6 +702,17 @@ namespace Content.Shared.GameObjects.Components.Body } } } + + public virtual void Gib(bool gibParts = false) + { + foreach (var (_, part) in Parts) + { + RemovePart(part); + + if (gibParts) + part.Gib(); + } + } } [Serializable, NetSerializable] diff --git a/Resources/Audio/Effects/gib1.ogg b/Resources/Audio/Effects/gib1.ogg new file mode 100644 index 0000000000..31c1a01cd6 Binary files /dev/null and b/Resources/Audio/Effects/gib1.ogg differ diff --git a/Resources/Audio/Effects/gib2.ogg b/Resources/Audio/Effects/gib2.ogg new file mode 100644 index 0000000000..68e2499208 Binary files /dev/null and b/Resources/Audio/Effects/gib2.ogg differ diff --git a/Resources/Audio/Effects/gib3.ogg b/Resources/Audio/Effects/gib3.ogg new file mode 100644 index 0000000000..aa37a543a9 Binary files /dev/null and b/Resources/Audio/Effects/gib3.ogg differ diff --git a/Resources/Prototypes/Body/Mechanisms/basic_human_organs.yml b/Resources/Prototypes/Body/Mechanisms/basic_human_organs.yml index cf40b346d0..d256e7187e 100644 --- a/Resources/Prototypes/Body/Mechanisms/basic_human_organs.yml +++ b/Resources/Prototypes/Body/Mechanisms/basic_human_organs.yml @@ -1,5 +1,24 @@ +- type: entity + id: BaseMechanism + parent: BaseItem + name: "base mechanism" + abstract: true + components: + - type: Mechanism + +- type: entity + id: BaseHumanOrgan + parent: BaseMechanism + name: "base human organ" + abstract: true + components: + - type: Sprite + netsync: false + sprite: Mobs/Species/Human/organs.rsi + - type: entity id: BrainHuman + parent: BaseHumanOrgan name: "human brain" description: "The source of incredible, unending intelligence. Honk." components: @@ -16,6 +35,7 @@ - type: entity id: EyesHuman + parent: BaseHumanOrgan name: "human eyes" description: "Ocular organ capable of turning light into a colorful visual." components: @@ -30,6 +50,7 @@ - type: entity id: HeartHuman + parent: BaseHumanOrgan name: "human heart" description: "Pumps blood throughout a body. Essential for any entity with blood." components: @@ -46,6 +67,7 @@ - type: entity id: LungsHuman + parent: BaseHumanOrgan name: "human lungs" description: "Filters oxygen from an atmosphere, which is then sent into the bloodstream to be used as an electron carrier." components: @@ -62,6 +84,7 @@ - type: entity id: StomachHuman + parent: BaseHumanOrgan name: "human stomach" description: "Gross. This is hard to stomach." components: @@ -82,6 +105,7 @@ - type: entity id: LiverHuman + parent: BaseHumanOrgan name: "human liver" description: "Filters impurities out of a bloodstream and provides other important functionality to a human." components: @@ -96,6 +120,7 @@ - type: entity id: KidneysHuman + parent: BaseHumanOrgan name: "human kidneys" description: "Filters toxins out of a bloodstream." components: diff --git a/Resources/Prototypes/Body/Parts/humanoid_parts.yml b/Resources/Prototypes/Body/Parts/humanoid_parts.yml index 4852b5b86b..a6036e559f 100644 --- a/Resources/Prototypes/Body/Parts/humanoid_parts.yml +++ b/Resources/Prototypes/Body/Parts/humanoid_parts.yml @@ -1,6 +1,7 @@ # TODO BODY: Part damage - type: entity id: PartHuman + parent: BaseItem name: "human body part" abstract: true diff --git a/Resources/Prototypes/Entities/Mobs/Species/human.yml b/Resources/Prototypes/Entities/Mobs/Species/human.yml index 21c5c58232..b9a8beff86 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/human.yml @@ -161,6 +161,11 @@ 0: !type:NormalMobState {} 100: !type:CriticalMobState {} 200: !type:DeadMobState {} + - type: Destructible + thresholds: + 400: + behaviors: + - !type:GibBehavior { } - type: HeatResistance - type: Appearance visuals: diff --git a/Resources/Prototypes/Entities/Objects/Fun/toys.yml b/Resources/Prototypes/Entities/Objects/Fun/toys.yml index 0d61da304d..b6212ddf61 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/toys.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/toys.yml @@ -528,7 +528,6 @@ - type: Sprite sprite: Objects/Fun/toys.rsi state: foamblade - - type: MeleeWeapon range: 2.0 arcwidth: 0 @@ -539,6 +538,15 @@ HeldPrefix: foamblade - type: ItemCooldown +- type: entity + name: foamblade + parent: FoamBlade + id: FoamBladeAdminbus + suffix: adminbused + components: + - type: MeleeWeapon + damage: 1000 + # MISC - type: entity diff --git a/Resources/Prototypes/SoundCollections/gib.yml b/Resources/Prototypes/SoundCollections/gib.yml new file mode 100644 index 0000000000..5c39aacb3f --- /dev/null +++ b/Resources/Prototypes/SoundCollections/gib.yml @@ -0,0 +1,6 @@ +- type: soundCollection + id: gib + files: + - /Audio/Effects/gib1.ogg + - /Audio/Effects/gib2.ogg + - /Audio/Effects/gib3.ogg diff --git a/SpaceStation14.sln.DotSettings b/SpaceStation14.sln.DotSettings index 29ca78c389..c8f876874b 100644 --- a/SpaceStation14.sln.DotSettings +++ b/SpaceStation14.sln.DotSettings @@ -103,6 +103,7 @@ True True True + True True True True