diff --git a/Content.Server/Chat/ChatCommands.cs b/Content.Server/Chat/ChatCommands.cs index 8e299b3df5..99a6d06963 100644 --- a/Content.Server/Chat/ChatCommands.cs +++ b/Content.Server/Chat/ChatCommands.cs @@ -1,10 +1,16 @@ -using Content.Server.GameObjects.Components.Observer; +using Content.Server.GameObjects; +using Content.Server.GameObjects.Components.Observer; using Content.Server.Interfaces.Chat; +using Content.Server.Interfaces.GameObjects; using Content.Server.Players; +using Content.Shared.GameObjects; using Robust.Server.Interfaces.Console; using Robust.Server.Interfaces.Player; using Robust.Shared.Enums; +using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.IoC; +using Robust.Shared.Localization; +using System.Linq; namespace Content.Server.Chat { @@ -72,4 +78,81 @@ namespace Content.Server.Chat chat.SendOOC(player, string.Join(" ", args)); } } + + internal class SuicideCommand : IClientCommand + { + public string Command => "suicide"; + + public string Description => "Commits suicide"; + + public string Help => "The suicide command gives you a quick way out of a round while remaining in-character.\n" + + "The method varies, first it will attempt to use the held item in your active hand.\n" + + "If that fails, it will attempt to use an object in the environment.\n" + + "Finally, if neither of the above worked, you will die by biting your tongue."; + + private void DealDamage(ISuicideAct suicide, IChatManager chat, DamageableComponent damageableComponent, IEntity source, IEntity target) + { + SuicideKind kind = suicide.Suicide(target, chat); + if (kind != SuicideKind.Special) + { + damageableComponent.TakeDamage(kind switch + { + SuicideKind.Brute => DamageType.Brute, + SuicideKind.Heat => DamageType.Heat, + SuicideKind.Cold => DamageType.Cold, + SuicideKind.Acid => DamageType.Acid, + SuicideKind.Toxic => DamageType.Toxic, + SuicideKind.Electric => DamageType.Electric, + _ => DamageType.Brute + }, + 500, //TODO: needs to be a max damage of some sorts + source, + target); + } + } + + public void Execute(IConsoleShell shell, IPlayerSession player, string[] args) + { + if (player.Status != SessionStatus.InGame) + return; + + var chat = IoCManager.Resolve(); + var owner = player.ContentData().Mind.OwnedMob.Owner; + var dmgComponent = owner.GetComponent(); + //TODO: needs to check if the mob is actually alive + //TODO: maybe set a suicided flag to prevent ressurection? + + // Held item suicide + var handsComponent = owner.GetComponent(); + var itemComponent = handsComponent.GetActiveHand; + if (itemComponent != null) + { + ISuicideAct suicide = itemComponent.Owner.GetAllComponents().FirstOrDefault(); + if (suicide != null) + { + DealDamage(suicide, chat, dmgComponent, itemComponent.Owner, owner); + return; + } + } + // Get all entities in range of the suicider + var entities = owner.EntityManager.GetEntitiesInRange(owner, 1, true); + if (entities.Count() > 0) + { + foreach (var entity in entities) + { + if (entity.HasComponent()) + continue; + var suicide = entity.GetAllComponents().FirstOrDefault(); + if (suicide != null) + { + DealDamage(suicide, chat, dmgComponent, entity, owner); + return; + } + } + } + // Default suicide, bite your tongue + chat.EntityMe(owner, Loc.GetString("is attempting to bite {0:their} own tongue, looks like {0:theyre} trying to commit suicide!", owner)); //TODO: theyre macro + dmgComponent.TakeDamage(DamageType.Brute, 500, owner, owner); //TODO: dmg value needs to be a max damage of some sorts + } + } } diff --git a/Content.Server/GameObjects/Components/Interactable/WelderComponent.cs b/Content.Server/GameObjects/Components/Interactable/WelderComponent.cs index dc18cbd09a..c58c2bc7f9 100644 --- a/Content.Server/GameObjects/Components/Interactable/WelderComponent.cs +++ b/Content.Server/GameObjects/Components/Interactable/WelderComponent.cs @@ -1,10 +1,14 @@ using System; +using System.Runtime.Remoting; using Content.Server.GameObjects.Components.Chemistry; using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces; +using Content.Server.Interfaces.Chat; +using Content.Server.Interfaces.GameObjects; using Content.Shared.Chemistry; using Content.Shared.GameObjects; using Content.Shared.GameObjects.Components.Interactable; +using Content.Shared.Interfaces; using Robust.Server.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; @@ -18,7 +22,7 @@ namespace Content.Server.GameObjects.Components.Interactable { [RegisterComponent] [ComponentReference(typeof(ToolComponent))] - public class WelderComponent : ToolComponent, IExamine, IUse + public class WelderComponent : ToolComponent, IExamine, IUse, ISuicideAct { #pragma warning disable 649 [Dependency] private IEntitySystemManager _entitySystemManager; @@ -92,17 +96,18 @@ namespace Content.Server.GameObjects.Components.Interactable return base.UseTool(user, target, toolQualityNeeded) && TryWeld(fuelConsumed, user); } - private bool TryWeld(float value, IEntity user = null) + private bool TryWeld(float value, IEntity user = null, bool silent = false) { if (!WelderLit) { - _notifyManager.PopupMessage(Owner, user, Loc.GetString("The welder is turned off!")); + if(!silent) _notifyManager.PopupMessage(Owner, user, Loc.GetString("The welder is turned off!")); return false; } if (!CanWeld(value)) { - _notifyManager.PopupMessage(Owner, user, Loc.GetString("The welder does not have enough fuel for that!")); + if(!silent) _notifyManager.PopupMessage(Owner, user, Loc.GetString("The welder does not have enough fuel for that!")); + return false; } if (_solutionComponent == null) @@ -185,5 +190,17 @@ namespace Content.Server.GameObjects.Components.Interactable Dirty(); } + + public SuicideKind Suicide(IEntity victim, IChatManager chat) + { + if (TryWeld(5, victim, silent: true)) + { + PlaySoundCollection("Welder", -5); + chat.EntityMe(victim, Loc.GetString("welds {0:their} every orifice closed! It looks like {0:theyre} trying to commit suicide!", victim)); //TODO: theyre macro + return SuicideKind.Heat; + } + chat.EntityMe(victim, Loc.GetString("bashes {0:themselves} with the {1}!", victim, Owner.Name)); + return SuicideKind.Brute; + } } } diff --git a/Content.Server/GameObjects/Components/Kitchen/KitchenMicrowaveComponent.cs b/Content.Server/GameObjects/Components/Kitchen/KitchenMicrowaveComponent.cs index 939881fb92..ade3d36fd4 100644 --- a/Content.Server/GameObjects/Components/Kitchen/KitchenMicrowaveComponent.cs +++ b/Content.Server/GameObjects/Components/Kitchen/KitchenMicrowaveComponent.cs @@ -23,12 +23,16 @@ using Robust.Shared.Localization; using Content.Server.Interfaces; using Content.Server.Utility; using Robust.Shared.Audio; +using Content.Server.Interfaces.GameObjects; +using Content.Server.Interfaces.Chat; +using Content.Server.BodySystem; +using Content.Shared.BodySystem; namespace Content.Server.GameObjects.Components.Kitchen { [RegisterComponent] [ComponentReference(typeof(IActivate))] - public class KitchenMicrowaveComponent : SharedMicrowaveComponent, IActivate, IInteractUsing, ISolutionChange + public class KitchenMicrowaveComponent : SharedMicrowaveComponent, IActivate, IInteractUsing, ISolutionChange, ISuicideAct { #pragma warning disable 649 [Dependency] private readonly IEntitySystemManager _entitySystemManager; @@ -403,5 +407,25 @@ namespace Content.Server.GameObjects.Components.Kitchen } + public SuicideKind Suicide(IEntity victim, IChatManager chat) + { + int headCount = 0; + if (victim.TryGetComponent(out var bodyManagerComponent)) + { + var heads = bodyManagerComponent.GetBodyPartsOfType(BodyPartType.Head); + foreach (var head in heads) + { + var droppedHead = bodyManagerComponent.DisconnectBodyPart(head, true); + _storage.Insert(droppedHead); + headCount++; + } + } + chat.EntityMe(victim, Loc.GetPluralString("is trying to cook {0:their} head!", "is trying to cook {0:their} heads!", headCount, victim)); + _currentCookTimerTime = 10; + ClickSound(); + UpdateUserInterface(); + wzhzhzh(); + return SuicideKind.Heat; + } } } diff --git a/Content.Server/Health/BodySystem/BodyManagerComponent.cs b/Content.Server/Health/BodySystem/BodyManagerComponent.cs index 514ad33df1..c4027faa5b 100644 --- a/Content.Server/Health/BodySystem/BodyManagerComponent.cs +++ b/Content.Server/Health/BodySystem/BodyManagerComponent.cs @@ -189,12 +189,13 @@ namespace Content.Server.BodySystem { } /// - /// Disconnects the given BodyPart reference, potentially dropping other BodyParts if they were hanging off it. + /// Disconnects the given BodyPart reference, potentially dropping other BodyParts if they were hanging off it. /// - public void DisconnectBodyPart(BodyPart part, bool dropEntity) + /// Returns the dropped entity, or null if no part is dropped + public IEntity DisconnectBodyPart(BodyPart part, bool dropEntity) { if (!_partDictionary.ContainsValue(part)) - return; + return null; if (part != null) { string slotName = _partDictionary.FirstOrDefault(x => x.Value == part).Key; @@ -213,8 +214,10 @@ namespace Content.Server.BodySystem { { var partEntity = Owner.EntityManager.SpawnEntity("BaseDroppedBodyPart", Owner.Transform.GridPosition); partEntity.GetComponent().TransferBodyPartData(part); + return partEntity; } } + return null; } /// diff --git a/Content.Server/Interfaces/GameObjects/ISuicideAct.cs b/Content.Server/Interfaces/GameObjects/ISuicideAct.cs new file mode 100644 index 0000000000..8d09775b86 --- /dev/null +++ b/Content.Server/Interfaces/GameObjects/ISuicideAct.cs @@ -0,0 +1,26 @@ +using Content.Server.Interfaces.Chat; +using Robust.Shared.Interfaces.GameObjects; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Content.Server.Interfaces.GameObjects +{ + public interface ISuicideAct + { + public SuicideKind Suicide(IEntity victim, IChatManager chat); + } + + public enum SuicideKind + { + Special, //Doesn't damage the mob, used for "weird" suicides like gibbing + + //Damage type suicides + Brute, + Heat, + Cold, + Acid, + Toxic, + Electric + } +} diff --git a/Resources/Groups/groups.yml b/Resources/Groups/groups.yml index 5900002638..9d11fce6d0 100644 --- a/Resources/Groups/groups.yml +++ b/Resources/Groups/groups.yml @@ -12,6 +12,7 @@ - observe - toggleready - ghost + - suicide - Index: 50 Name: Moderator @@ -28,6 +29,7 @@ - observe - toggleready - ghost + - suicide - kick - listplayers - loc @@ -47,6 +49,7 @@ - observe - toggleready - ghost + - suicide - spawn - delete - tp @@ -89,6 +92,7 @@ - observe - toggleready - ghost + - suicide - spawn - delete - tp