diff --git a/Content.Server/Act/ISuicideAct.cs b/Content.Server/Act/ISuicideAct.cs deleted file mode 100644 index 6865e915eabe..000000000000 --- a/Content.Server/Act/ISuicideAct.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Content.Server.Chat.Managers; -using Robust.Shared.Analyzers; -using Robust.Shared.GameObjects; - -namespace Content.Server.Act -{ - [RequiresExplicitImplementation] - public interface ISuicideAct - { - public SuicideKind Suicide(EntityUid victim, IChatManager chat); - } - - public enum SuicideKind - { - Special, //Doesn't damage the mob, used for "weird" suicides like gibbing - - //Damage type suicides - Blunt, - Slash, - Piercing, - Heat, - Shock, - Cold, - Poison, - Radiation, - Asphyxiation, - Bloodloss - } -} diff --git a/Content.Server/Chat/ChatSystem.cs b/Content.Server/Chat/ChatSystem.cs index 72f8a8ed629c..4ca731faca65 100644 --- a/Content.Server/Chat/ChatSystem.cs +++ b/Content.Server/Chat/ChatSystem.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Text; using Content.Server.Administration.Logs; using Content.Server.Administration.Managers; diff --git a/Content.Server/Chat/Commands/SuicideCommand.cs b/Content.Server/Chat/Commands/SuicideCommand.cs index c82dd8a13c48..916dd6326137 100644 --- a/Content.Server/Chat/Commands/SuicideCommand.cs +++ b/Content.Server/Chat/Commands/SuicideCommand.cs @@ -36,122 +36,9 @@ internal sealed class SuicideCommand : IConsoleCommand public string Help => Loc.GetString("suicide-command-help-text"); - private void DealDamage(ISuicideAct suicide, IChatManager chat, EntityUid target) - { - var kind = suicide.Suicide(target, chat); - if (kind != SuicideKind.Special) - { - // TODO SUICIDE ..heh.. anyway, someone should fix this mess. - var prototypeManager = IoCManager.Resolve(); - DamageSpecifier damage = new(kind switch - { - SuicideKind.Blunt => prototypeManager.Index("Blunt"), - SuicideKind.Slash => prototypeManager.Index("Slash"), - SuicideKind.Piercing => prototypeManager.Index("Piercing"), - SuicideKind.Heat => prototypeManager.Index("Heat"), - SuicideKind.Shock => prototypeManager.Index("Shock"), - SuicideKind.Cold => prototypeManager.Index("Cold"), - SuicideKind.Poison => prototypeManager.Index("Poison"), - SuicideKind.Radiation => prototypeManager.Index("Radiation"), - SuicideKind.Asphyxiation => prototypeManager.Index("Asphyxiation"), - SuicideKind.Bloodloss => prototypeManager.Index("Bloodloss"), - _ => prototypeManager.Index("Blunt") - }, - 200); - EntitySystem.Get().TryChangeDamage(target, damage, true); - } - } - public void Execute(IConsoleShell shell, string argStr, string[] args) { - var player = shell.Player as IPlayerSession; - if (player == null) - { - shell.WriteLine(Loc.GetString("shell-cannot-run-command-from-server")); - return; - } - - if (player.Status != SessionStatus.InGame || player.AttachedEntity == null) - return; - - var chat = IoCManager.Resolve(); - var mind = player.ContentData()?.Mind; - - // This check also proves mind not-null for at the end when the mob is ghosted. - if (mind?.OwnedComponent?.Owner is not {Valid: true} owner) - { - shell.WriteLine("You don't have a mind!"); - return; - } - - //Checks to see if the player is dead. - if(_entities.TryGetComponent(owner, out var mobState) && mobState.IsDead()) - { - shell.WriteLine(Loc.GetString("suicide-command-already-dead")); - return; - } - - //Checks to see if the CannotSuicide tag exits, ghosts instead. - if(EntitySystem.Get().HasTag(owner, "CannotSuicide")) - { - if (!EntitySystem.Get().OnGhostAttempt(mind, true)) - { - shell?.WriteLine("You can't ghost right now."); - return; - } - return; - } - - //TODO: needs to check if the mob is actually alive - //TODO: maybe set a suicided flag to prevent resurrection? - - EntitySystem.Get().Add(LogType.Suicide, - $"{_entities.ToPrettyString(player.AttachedEntity.Value):player} is committing suicide"); - - // Held item suicide - if (_entities.TryGetComponent(owner, out HandsComponent handsComponent) - && handsComponent.ActiveHandEntity is EntityUid item) - { - var suicide = _entities.GetComponents(item).FirstOrDefault(); - - if (suicide != null) - { - DealDamage(suicide, chat, owner); - return; - } - } - - // Get all entities in range of the suicider - var entities = EntitySystem.Get().GetEntitiesInRange(owner, 1, LookupFlags.Approximate | LookupFlags.Anchored).ToArray(); - - if (entities.Length > 0) - { - foreach (var entity in entities) - { - if (_entities.HasComponent(entity)) - continue; - var suicide = _entities.GetComponents(entity).FirstOrDefault(); - if (suicide != null) - { - DealDamage(suicide, chat, owner); - return; - } - } - } - - // Default suicide, bite your tongue - var othersMessage = Loc.GetString("suicide-command-default-text-others",("name", owner)); - owner.PopupMessageOtherClients(othersMessage); - - var selfMessage = Loc.GetString("suicide-command-default-text-self"); - owner.PopupMessage(selfMessage); - - DamageSpecifier damage = new(IoCManager.Resolve().Index("Bloodloss"), 200); - EntitySystem.Get().TryChangeDamage(owner, damage, true); - - // Prevent the player from returning to the body. - // Note that mind cannot be null because otherwise owner would be null. - EntitySystem.Get().OnGhostAttempt(mind!, false); + EntitySystem.Get().Suicide(shell); } } } diff --git a/Content.Server/Chat/SuicideSystem.cs b/Content.Server/Chat/SuicideSystem.cs new file mode 100644 index 000000000000..2dc5cc33e5bc --- /dev/null +++ b/Content.Server/Chat/SuicideSystem.cs @@ -0,0 +1,150 @@ +using Content.Server.Act; +using Content.Server.Administration.Logs; +using Content.Server.Chat.Managers; +using Content.Server.GameTicking; +using Content.Server.Hands.Components; +using Content.Server.Players; +using Content.Server.Popups; +using Content.Shared.Damage; +using Content.Shared.Damage.Prototypes; +using Content.Shared.Database; +using Content.Shared.Interaction.Events; +using Content.Shared.Item; +using Content.Shared.MobState.Components; +using Content.Shared.Popups; +using Content.Shared.Tag; +using Robust.Server.Player; +using Robust.Shared.Console; +using Robust.Shared.Enums; +using Robust.Shared.Prototypes; +using System.Linq; + +namespace Content.Server.Chat +{ + public sealed class SuicideSystem : EntitySystem + { + [Dependency] private readonly DamageableSystem _damageableSystem = default!; + [Dependency] private readonly IChatManager _chatManager = default!; + [Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!; + [Dependency] private readonly AdminLogSystem _adminLogSystem = default!; + [Dependency] private readonly TagSystem _tagSystem = default!; + [Dependency] private readonly GameTicker _gameTicker = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + + public void Suicide(IConsoleShell shell) + { + //TODO: Make this work without the console shell + + var player = shell.Player as IPlayerSession; + if (player == null) + { + shell.WriteLine(Loc.GetString("shell-cannot-run-command-from-server")); + return; + } + + if (player.Status != SessionStatus.InGame || player.AttachedEntity == null) + return; + var mind = player.ContentData()?.Mind; + + // This check also proves mind not-null for at the end when the mob is ghosted. + if (mind?.OwnedComponent?.Owner is not { Valid: true } owner) + { + shell.WriteLine("You don't have a mind!"); + return; + } + + //Checks to see if the player is dead. + if (EntityManager.TryGetComponent(owner, out var mobState) && mobState.IsDead()) + { + shell.WriteLine(Loc.GetString("suicide-command-already-dead")); + return; + } + + //Checks to see if the CannotSuicide tag exits, ghosts instead. + if (_tagSystem.HasTag(owner, "CannotSuicide")) + { + if (!_gameTicker.OnGhostAttempt(mind, true)) + { + shell?.WriteLine("You can't ghost right now."); + return; + } + return; + } + + //TODO: needs to check if the mob is actually alive + //TODO: maybe set a suicided flag to prevent resurrection? + + _adminLogSystem.Add(LogType.Suicide, + $"{EntityManager.ToPrettyString(player.AttachedEntity.Value):player} is committing suicide"); + + var suicideEvent = new SuicideEvent(owner); + // Held item suicide + if (EntityManager.TryGetComponent(owner, out HandsComponent handsComponent) + && handsComponent.ActiveHandEntity is EntityUid item) + { + RaiseLocalEvent(item, suicideEvent, false); + + if (suicideEvent.Handled) + { + ApplyDeath(owner, suicideEvent.Kind!.Value); + return; + } + } + + // Get all entities in range of the suicider + var entities = _entityLookupSystem.GetEntitiesInRange(owner, 1, LookupFlags.Approximate | LookupFlags.Anchored).ToArray(); + + if (entities.Length > 0) + { + foreach (var entity in entities) + { + if (EntityManager.HasComponent(entity)) + continue; + RaiseLocalEvent(entity, suicideEvent, false); + + if (suicideEvent.Handled) + { + ApplyDeath(owner, suicideEvent.Kind!.Value); + return; + } + } + } + + // Default suicide, bite your tongue + var othersMessage = Loc.GetString("suicide-command-default-text-others", ("name", owner)); + owner.PopupMessageOtherClients(othersMessage); + + var selfMessage = Loc.GetString("suicide-command-default-text-self"); + owner.PopupMessage(selfMessage); + + ApplyDeath(owner, SuicideKind.Bloodloss); + + // Prevent the player from returning to the body. + // Note that mind cannot be null because otherwise owner would be null. + _gameTicker.OnGhostAttempt(mind!, false); + } + + private void ApplyDeath(EntityUid target, SuicideKind kind) + { + if (kind == SuicideKind.Special) return; + // TODO SUICIDE ..heh.. anyway, someone should fix this mess. + DamageSpecifier damage = new(kind switch + { + SuicideKind.Blunt => _prototypeManager.Index("Blunt"), + SuicideKind.Slash => _prototypeManager.Index("Slash"), + SuicideKind.Piercing => _prototypeManager.Index("Piercing"), + SuicideKind.Heat => _prototypeManager.Index("Heat"), + SuicideKind.Shock => _prototypeManager.Index("Shock"), + SuicideKind.Cold => _prototypeManager.Index("Cold"), + SuicideKind.Poison => _prototypeManager.Index("Poison"), + SuicideKind.Radiation => _prototypeManager.Index("Radiation"), + SuicideKind.Asphyxiation => _prototypeManager.Index("Asphyxiation"), + SuicideKind.Bloodloss => _prototypeManager.Index("Bloodloss"), + _ => _prototypeManager.Index("Blunt") + }, + 200); + + _damageableSystem.TryChangeDamage(target, damage, true); + } + } +} diff --git a/Content.Server/Kitchen/Components/KitchenSpikeComponent.cs b/Content.Server/Kitchen/Components/KitchenSpikeComponent.cs index a1972a66d2fd..3ed4e1d02579 100644 --- a/Content.Server/Kitchen/Components/KitchenSpikeComponent.cs +++ b/Content.Server/Kitchen/Components/KitchenSpikeComponent.cs @@ -1,19 +1,11 @@ -using Content.Server.Act; -using Content.Server.Chat.Managers; using Content.Server.Kitchen.EntitySystems; -using Content.Server.Popups; using Content.Shared.DragDrop; using Content.Shared.Kitchen.Components; -using Content.Shared.Popups; -using Robust.Shared.Analyzers; -using Robust.Shared.GameObjects; -using Robust.Shared.Localization; -using System.Threading; namespace Content.Server.Kitchen.Components { [RegisterComponent, Friend(typeof(KitchenSpikeSystem))] - public sealed class KitchenSpikeComponent : SharedKitchenSpikeComponent, ISuicideAct + public sealed class KitchenSpikeComponent : SharedKitchenSpikeComponent { public List? PrototypesToSpawn; @@ -30,17 +22,5 @@ public override bool DragDropOn(DragDropEvent eventArgs) { return true; } - - // ECS this out!, Handleable SuicideEvent? - SuicideKind ISuicideAct.Suicide(EntityUid victim, IChatManager chat) - { - var othersMessage = Loc.GetString("comp-kitchen-spike-suicide-other", ("victim", victim)); - victim.PopupMessageOtherClients(othersMessage); - - var selfMessage = Loc.GetString("comp-kitchen-spike-suicide-self"); - victim.PopupMessage(selfMessage); - - return SuicideKind.Piercing; - } } } diff --git a/Content.Server/Kitchen/Components/MicrowaveComponent.cs b/Content.Server/Kitchen/Components/MicrowaveComponent.cs index bd1d6966e986..38bb42950c6a 100644 --- a/Content.Server/Kitchen/Components/MicrowaveComponent.cs +++ b/Content.Server/Kitchen/Components/MicrowaveComponent.cs @@ -25,7 +25,7 @@ namespace Content.Server.Kitchen.Components { [RegisterComponent] - public sealed class MicrowaveComponent : SharedMicrowaveComponent, ISuicideAct + public sealed class MicrowaveComponent : SharedMicrowaveComponent { [Dependency] private readonly IEntityManager _entities = default!; @@ -98,6 +98,12 @@ protected override void Initialize() } } + public void SetCookTime(uint cookTime) + { + _currentCookTimerTime = cookTime; + UIDirty = true; + } + private void UserInterfaceOnReceiveMessage(ServerBoundUserInterfaceMessage message) { if (!Powered || _busy) @@ -193,7 +199,7 @@ public void SetAppearance(MicrowaveVisualState state) // ReSharper disable once InconsistentNaming // ReSharper disable once IdentifierTypo - private void Wzhzhzh() + public void Wzhzhzh() { if (!HasContents) { @@ -439,60 +445,10 @@ private bool CanSatisfyRecipe(FoodRecipePrototype recipe, Dictionary(victim, out var body)) - { - var headSlots = body.GetSlotsOfType(BodyPartType.Head); - - foreach (var slot in headSlots) - { - var part = slot.Part; - - if (part == null || - !body.TryDropPart(slot, out var dropped)) - { - continue; - } - - foreach (var droppedPart in dropped.Values) - { - if (droppedPart.PartType != BodyPartType.Head) - { - continue; - } - - Storage.Insert(droppedPart.Owner); - headCount++; - } - } - } - - var othersMessage = headCount > 1 - ? Loc.GetString("microwave-component-suicide-multi-head-others-message", ("victim", victim)) - : Loc.GetString("microwave-component-suicide-others-message", ("victim", victim)); - - victim.PopupMessageOtherClients(othersMessage); - - var selfMessage = headCount > 1 - ? Loc.GetString("microwave-component-suicide-multi-head-message") - : Loc.GetString("microwave-component-suicide-message"); - - victim.PopupMessage(selfMessage); - - _currentCookTimerTime = 10; - ClickSound(); - UIDirty = true; - Wzhzhzh(); - return SuicideKind.Heat; - } } public sealed class BeingMicrowavedEvent : HandledEntityEventArgs diff --git a/Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs b/Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs index 99e37e2f6f70..9f8ffcfb9d19 100644 --- a/Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs @@ -15,6 +15,8 @@ using Content.Shared.Storage; using Robust.Shared.Random; using static Content.Shared.Kitchen.Components.SharedKitchenSpikeComponent; +using Content.Shared.Interaction.Events; +using Content.Shared.Popups; namespace Content.Server.Kitchen.EntitySystems { @@ -35,6 +37,20 @@ public override void Initialize() //DoAfter SubscribeLocalEvent(OnSpikingFinished); SubscribeLocalEvent(OnSpikingFail); + + SubscribeLocalEvent(OnSuicide); + } + + private void OnSuicide(EntityUid uid, KitchenSpikeComponent component, SuicideEvent args) + { + if (args.Handled) return; + args.SetHandled(SuicideKind.Piercing); + var victim = args.Victim; + var othersMessage = Loc.GetString("comp-kitchen-spike-suicide-other", ("victim", victim)); + victim.PopupMessageOtherClients(othersMessage); + + var selfMessage = Loc.GetString("comp-kitchen-spike-suicide-self"); + victim.PopupMessage(selfMessage); } private void OnSpikingFail(EntityUid uid, KitchenSpikeComponent component, SpikingFailEvent args) diff --git a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs index 288b3b2ff506..92c1632cd150 100644 --- a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs @@ -7,6 +7,10 @@ using Content.Shared.Kitchen.Components; using Robust.Shared.Player; using JetBrains.Annotations; +using Content.Shared.Interaction.Events; +using Content.Shared.Body.Components; +using Content.Shared.Body.Part; +using Content.Shared.Popups; namespace Content.Server.Kitchen.EntitySystems { @@ -21,6 +25,56 @@ public override void Initialize() SubscribeLocalEvent(OnSolutionChange); SubscribeLocalEvent(OnInteractUsing); SubscribeLocalEvent(OnBreak); + SubscribeLocalEvent(OnSuicide); + } + + private void OnSuicide(EntityUid uid, MicrowaveComponent component, SuicideEvent args) + { + if (args.Handled) return; + args.SetHandled(SuicideKind.Heat); + var victim = args.Victim; + var headCount = 0; + + if (TryComp(victim, out var body)) + { + var headSlots = body.GetSlotsOfType(BodyPartType.Head); + + foreach (var slot in headSlots) + { + var part = slot.Part; + + if (part == null || + !body.TryDropPart(slot, out var dropped)) + { + continue; + } + + foreach (var droppedPart in dropped.Values) + { + if (droppedPart.PartType != BodyPartType.Head) + { + continue; + } + component.Storage.Insert(droppedPart.Owner); + headCount++; + } + } + } + + var othersMessage = headCount > 1 + ? Loc.GetString("microwave-component-suicide-multi-head-others-message", ("victim", victim)) + : Loc.GetString("microwave-component-suicide-others-message", ("victim", victim)); + + victim.PopupMessageOtherClients(othersMessage); + + var selfMessage = headCount > 1 + ? Loc.GetString("microwave-component-suicide-multi-head-message") + : Loc.GetString("microwave-component-suicide-message"); + + victim.PopupMessage(selfMessage); + component.ClickSound(); + component.SetCookTime(10); + component.Wzhzhzh(); } private void OnSolutionChange(EntityUid uid, MicrowaveComponent component, SolutionChangedEvent args) diff --git a/Content.Server/Morgue/Components/CrematoriumEntityStorageComponent.cs b/Content.Server/Morgue/Components/CrematoriumEntityStorageComponent.cs index d47719d4d7eb..7d601d5df9fb 100644 --- a/Content.Server/Morgue/Components/CrematoriumEntityStorageComponent.cs +++ b/Content.Server/Morgue/Components/CrematoriumEntityStorageComponent.cs @@ -22,7 +22,7 @@ namespace Content.Server.Morgue.Components [ComponentReference(typeof(IActivate))] [ComponentReference(typeof(IStorageComponent))] #pragma warning disable 618 - public sealed class CrematoriumEntityStorageComponent : MorgueEntityStorageComponent, ISuicideAct + public sealed class CrematoriumEntityStorageComponent : MorgueEntityStorageComponent #pragma warning restore 618 { [Dependency] private readonly IEntityManager _entities = default!; @@ -100,34 +100,5 @@ public void Cremate() }, _cremateCancelToken.Token); } - - SuicideKind ISuicideAct.Suicide(EntityUid victim, IChatManager chat) - { - if (_entities.TryGetComponent(victim, out ActorComponent? actor) && actor.PlayerSession.ContentData()?.Mind is {} mind) - { - EntitySystem.Get().OnGhostAttempt(mind, false); - - if (mind.OwnedEntity is {Valid: true} entity) - { - entity.PopupMessage(Loc.GetString("crematorium-entity-storage-component-suicide-message")); - } - } - - victim.PopupMessageOtherClients(Loc.GetString("crematorium-entity-storage-component-suicide-message-others", ("victim", victim))); - - if (CanInsert(victim)) - { - Insert(victim); - EntitySystem.Get().Down(victim, false); - } - else - { - _entities.DeleteEntity(victim); - } - - Cremate(); - - return SuicideKind.Heat; - } } } diff --git a/Content.Server/Morgue/MorgueSystem.cs b/Content.Server/Morgue/MorgueSystem.cs index ef345c5c6eed..6146bd6a23b2 100644 --- a/Content.Server/Morgue/MorgueSystem.cs +++ b/Content.Server/Morgue/MorgueSystem.cs @@ -4,12 +4,22 @@ using Content.Shared.Database; using Content.Shared.Verbs; using JetBrains.Annotations; +using Content.Shared.Interaction.Events; +using Robust.Server.GameObjects; +using Content.Server.Players; +using Content.Server.GameTicking; +using Content.Server.Popups; +using Content.Shared.Standing; +using Robust.Shared.Player; namespace Content.Server.Morgue { [UsedImplicitly] public sealed class MorgueSystem : EntitySystem { + [Dependency] private readonly GameTicker _ticker = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly StandingStateSystem _stando = default!; private float _accumulatedFrameTime; @@ -19,9 +29,44 @@ public override void Initialize() SubscribeLocalEvent>(AddCremateVerb); SubscribeLocalEvent(OnCrematoriumExamined); + SubscribeLocalEvent(OnSuicide); SubscribeLocalEvent(OnMorgueExamined); } + private void OnSuicide(EntityUid uid, CrematoriumEntityStorageComponent component, SuicideEvent args) + { + if (args.Handled) return; + args.SetHandled(SuicideKind.Heat); + var victim = args.Victim; + if (TryComp(victim, out ActorComponent? actor) && actor.PlayerSession.ContentData()?.Mind is { } mind) + { + _ticker.OnGhostAttempt(mind, false); + + if (mind.OwnedEntity is { Valid: true } entity) + { + _popup.PopupEntity(Loc.GetString("crematorium-entity-storage-component-suicide-message"), entity, Filter.Pvs(entity, entityManager: EntityManager)); + } + } + + _popup.PopupEntity( + Loc.GetString("crematorium-entity-storage-component-suicide-message-others", ("victim", victim)), + victim, + Filter.Pvs(victim, entityManager: EntityManager).RemoveWhereAttachedEntity(e => e == victim)); + + if (component.CanInsert(victim)) + { + component.Insert(victim); + _stando.Down(victim, false); + } + else + { + + EntityManager.DeleteEntity(victim); + } + + component.Cremate(); + } + private void AddCremateVerb(EntityUid uid, CrematoriumEntityStorageComponent component, GetVerbsEvent args) { if (!args.CanAccess || !args.CanInteract || component.Cooking || component.Open) diff --git a/Content.Server/Recycling/Components/RecyclerComponent.cs b/Content.Server/Recycling/Components/RecyclerComponent.cs index 7236391a3aed..94f506995c8f 100644 --- a/Content.Server/Recycling/Components/RecyclerComponent.cs +++ b/Content.Server/Recycling/Components/RecyclerComponent.cs @@ -14,7 +14,7 @@ namespace Content.Server.Recycling.Components // TODO: Add sound and safe beep [RegisterComponent] [Friend(typeof(RecyclerSystem))] - public sealed class RecyclerComponent : Component, ISuicideAct + public sealed class RecyclerComponent : Component { [Dependency] private readonly IEntityManager _entMan = default!; @@ -44,27 +44,6 @@ private void Clean() } } - SuicideKind ISuicideAct.Suicide(EntityUid victim, IChatManager chat) - { - if (_entMan.TryGetComponent(victim, out ActorComponent? actor) && - actor.PlayerSession.ContentData()?.Mind is { } mind) - { - EntitySystem.Get().OnGhostAttempt(mind, false); - mind.OwnedEntity?.PopupMessage(Loc.GetString("recycler-component-suicide-message")); - } - - victim.PopupMessageOtherClients(Loc.GetString("recycler-component-suicide-message-others", - ("victim", victim))); - - if (_entMan.TryGetComponent(victim, out var body)) - { - body.Gib(true); - } - - EntitySystem.Get().Bloodstain(this); - return SuicideKind.Bloodloss; - } - /// /// Default sound to play when recycling /// diff --git a/Content.Server/Recycling/RecyclerSystem.cs b/Content.Server/Recycling/RecyclerSystem.cs index 640cc74100b5..eae1fb7d119e 100644 --- a/Content.Server/Recycling/RecyclerSystem.cs +++ b/Content.Server/Recycling/RecyclerSystem.cs @@ -1,11 +1,17 @@ using Content.Server.Audio; +using Content.Server.GameTicking; +using Content.Server.Players; +using Content.Server.Popups; using Content.Server.Power.Components; using Content.Server.Recycling.Components; using Content.Shared.Audio; using Content.Shared.Body.Components; using Content.Shared.Emag.Systems; +using Content.Shared.Interaction.Events; +using Content.Shared.Popups; using Content.Shared.Recycling; using Content.Shared.Tag; +using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Physics.Dynamics; using Robust.Shared.Player; @@ -17,6 +23,8 @@ public sealed class RecyclerSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly AmbientSoundSystem _ambience = default!; + [Dependency] private readonly GameTicker _ticker = default!; + [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly TagSystem _tags = default!; private const float RecyclerSoundCooldown = 0.8f; @@ -25,6 +33,34 @@ public override void Initialize() { SubscribeLocalEvent(OnCollide); SubscribeLocalEvent(OnEmagged); + SubscribeLocalEvent(OnSuicide); + } + + private void OnSuicide(EntityUid uid, RecyclerComponent component, SuicideEvent args) + { + if (args.Handled) return; + args.SetHandled(SuicideKind.Bloodloss); + var victim = args.Victim; + if (TryComp(victim, out ActorComponent? actor) && + actor.PlayerSession.ContentData()?.Mind is { } mind) + { + _ticker.OnGhostAttempt(mind, false); + if (mind.OwnedEntity is { Valid: true } entity) + { + _popup.PopupEntity(Loc.GetString("recycler-component-suicide-message"), entity, Filter.Pvs(entity, entityManager: EntityManager)); + } + } + + _popup.PopupEntity(Loc.GetString("recycler-component-suicide-message-others", ("victim", victim)), + victim, + Filter.Pvs(victim, entityManager: EntityManager).RemoveWhereAttachedEntity(e => e == victim)); + + if (TryComp(victim, out var body)) + { + body.Gib(true); + } + + Bloodstain(component); } public void EnableRecycler(RecyclerComponent component) diff --git a/Content.Server/Toilet/ToiletComponent.cs b/Content.Server/Toilet/ToiletComponent.cs index 560d6dfd5ffb..7d33b6fc48b0 100644 --- a/Content.Server/Toilet/ToiletComponent.cs +++ b/Content.Server/Toilet/ToiletComponent.cs @@ -1,15 +1,11 @@ -using Content.Server.Act; -using Content.Server.Chat.Managers; using Content.Shared.Sound; using Content.Shared.Tools; -using Robust.Shared.GameObjects; -using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Toilet { [RegisterComponent] - public sealed class ToiletComponent : Component, ISuicideAct + public sealed class ToiletComponent : Component { [DataField("pryLidTime")] public float PryLidTime = 1f; @@ -23,12 +19,5 @@ public sealed class ToiletComponent : Component, ISuicideAct public bool LidOpen = false; public bool IsSeatUp = false; public bool IsPrying = false; - - // todo: move me to ECS - SuicideKind ISuicideAct.Suicide(EntityUid victim, IChatManager chat) - { - return EntitySystem.Get().Suicide(Owner, victim, this); - } - } } diff --git a/Content.Server/Toilet/ToiletSystem.cs b/Content.Server/Toilet/ToiletSystem.cs index cc545a3d7520..0b5deced8b37 100644 --- a/Content.Server/Toilet/ToiletSystem.cs +++ b/Content.Server/Toilet/ToiletSystem.cs @@ -9,6 +9,7 @@ using Content.Shared.Body.Part; using Content.Shared.Examine; using Content.Shared.Interaction; +using Content.Shared.Interaction.Events; using Content.Shared.Toilet; using Content.Shared.Tools.Components; using Robust.Shared.Audio; @@ -35,10 +36,16 @@ public override void Initialize() SubscribeLocalEvent(OnInteractUsing); SubscribeLocalEvent(OnInteractHand); SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent(OnSuicide); SubscribeLocalEvent(OnToiletPried); SubscribeLocalEvent(OnToiletInterrupt); } + private void OnSuicide(EntityUid uid, ToiletComponent component, SuicideEvent args) + { + Suicide(component.Owner, uid, component); + } + private void OnInit(EntityUid uid, ToiletComponent component, ComponentInit args) { EntityManager.EnsureComponent(uid); diff --git a/Content.Shared/Interaction/Events/SuicideEvent.cs b/Content.Shared/Interaction/Events/SuicideEvent.cs new file mode 100644 index 000000000000..e0c57752617c --- /dev/null +++ b/Content.Shared/Interaction/Events/SuicideEvent.cs @@ -0,0 +1,39 @@ +namespace Content.Shared.Interaction.Events +{ + /// + /// Raised Directed at an entity to check whether they will handle the suicide. + /// + public sealed class SuicideEvent : EntityEventArgs + { + public SuicideEvent(EntityUid victim) + { + Victim = victim; + } + public void SetHandled(SuicideKind kind) + { + if (Handled) throw new InvalidOperationException("Suicide was already handled"); + Kind = kind; + } + + public SuicideKind? Kind { get; private set; } + public EntityUid Victim { get; private set; } + public bool Handled => Kind != null; + } + + public enum SuicideKind + { + Special, //Doesn't damage the mob, used for "weird" suicides like gibbing + + //Damage type suicides + Blunt, + Slash, + Piercing, + Heat, + Shock, + Cold, + Poison, + Radiation, + Asphyxiation, + Bloodloss + } +}