diff --git a/Content.Server/Commands/AttachBodyPartCommand.cs b/Content.Server/Commands/AttachBodyPartCommand.cs new file mode 100644 index 0000000000..b19c829611 --- /dev/null +++ b/Content.Server/Commands/AttachBodyPartCommand.cs @@ -0,0 +1,103 @@ +#nullable enable +using Content.Server.GameObjects.Components.Body.Part; +using Content.Shared.GameObjects.Components.Body; +using Content.Shared.GameObjects.Components.Body.Part; +using Robust.Server.Interfaces.Console; +using Robust.Server.Interfaces.Player; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; + +namespace Content.Server.Commands +{ + public class AttachBodyPartCommand : IClientCommand + { + public string Command => "attachbodypart"; + public string Description => "Attaches a body part to you or someone else."; + public string Help => $"{Command} / {Command} "; + + public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args) + { + var entityManager = IoCManager.Resolve(); + + IEntity entity; + EntityUid partUid; + + switch (args.Length) + { + case 1: + if (player == null) + { + shell.SendText(player, $"You need to specify an entity to attach the part to if you aren't a player.\n{Help}"); + return; + } + + if (player.AttachedEntity == null) + { + shell.SendText(player, $"You need to specify an entity to attach the part to if you aren't attached to an entity.\n{Help}"); + return; + } + + if (!EntityUid.TryParse(args[0], out partUid)) + { + shell.SendText(player, $"{args[0]} is not a valid entity uid."); + return; + } + + entity = player.AttachedEntity; + + break; + case 2: + if (!EntityUid.TryParse(args[0], out var entityUid)) + { + shell.SendText(player, $"{args[0]} is not a valid entity uid."); + return; + } + + if (!EntityUid.TryParse(args[1], out partUid)) + { + shell.SendText(player, $"{args[1]} is not a valid entity uid."); + return; + } + + if (!entityManager.TryGetEntity(entityUid, out var tempEntity)) + { + shell.SendText(player, $"{entityUid} is not a valid entity."); + return; + } + + entity = tempEntity; + break; + default: + shell.SendText(player, Help); + return; + } + + if (!entity.TryGetComponent(out IBody? body)) + { + shell.SendText(player, $"Entity {entity.Name} with uid {entity.Uid} does not have a {nameof(IBody)} component."); + return; + } + + if (!entityManager.TryGetEntity(partUid, out var partEntity)) + { + shell.SendText(player, $"{partUid} is not a valid entity."); + return; + } + + if (!partEntity.TryGetComponent(out IBodyPart? part)) + { + shell.SendText(player, $"Entity {partEntity.Name} with uid {args[0]} does not have a {nameof(IBodyPart)} component."); + return; + } + + if (body.HasPart(part)) + { + shell.SendText(player, $"Body part {partEntity.Name} with uid {partEntity.Uid} is already attached to entity {entity.Name} with uid {entity.Uid}"); + return; + } + + body.TryAddPart($"{nameof(BodyPartComponent.AttachBodyPartVerb)}-{partEntity.Uid}", part, true); + } + } +} diff --git a/Content.Server/GameObjects/Components/Body/Part/BodyPartComponent.cs b/Content.Server/GameObjects/Components/Body/Part/BodyPartComponent.cs index 8d5c568689..daa7a9c1bc 100644 --- a/Content.Server/GameObjects/Components/Body/Part/BodyPartComponent.cs +++ b/Content.Server/GameObjects/Components/Body/Part/BodyPartComponent.cs @@ -1,13 +1,16 @@ #nullable enable using System.Collections.Generic; using System.Linq; +using Content.Server.Commands; using Content.Server.Utility; using Content.Shared.GameObjects.Components.Body; using Content.Shared.GameObjects.Components.Body.Mechanism; using Content.Shared.GameObjects.Components.Body.Part; using Content.Shared.GameObjects.Components.Body.Surgery; +using Content.Shared.GameObjects.Verbs; using Content.Shared.Interfaces; using Content.Shared.Interfaces.GameObjects.Components; +using Robust.Server.Console; using Robust.Server.GameObjects; using Robust.Server.GameObjects.Components.Container; using Robust.Server.GameObjects.Components.UserInterface; @@ -15,6 +18,7 @@ using Robust.Server.Interfaces.GameObjects; using Robust.Server.Interfaces.Player; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Log; using Robust.Shared.ViewVariables; @@ -218,5 +222,54 @@ namespace Content.Server.GameObjects.Components.Body.Part break; } } + + [Verb] + public class AttachBodyPartVerb : Verb + { + protected override void GetData(IEntity user, BodyPartComponent component, VerbData data) + { + data.Visibility = VerbVisibility.Invisible; + + if (user == component.Owner) + { + return; + } + + if (!user.TryGetComponent(out IActorComponent? actor)) + { + return; + } + + var groupController = IoCManager.Resolve(); + + if (!groupController.CanCommand(actor.playerSession, "attachbodypart")) + { + return; + } + + if (!user.TryGetComponent(out IBody? body)) + { + return; + } + + if (body.HasPart(component)) + { + return; + } + + data.Visibility = VerbVisibility.Visible; + data.Text = Loc.GetString("Attach Body Part"); + } + + protected override void Activate(IEntity user, BodyPartComponent component) + { + if (!user.TryGetComponent(out IBody? body)) + { + return; + } + + body.TryAddPart($"{nameof(AttachBodyPartVerb)}-{component.Owner.Uid}", component, true); + } + } } } diff --git a/Content.Shared/GameObjects/Components/Body/IBody.cs b/Content.Shared/GameObjects/Components/Body/IBody.cs index 99f40dcd4e..9214436afd 100644 --- a/Content.Shared/GameObjects/Components/Body/IBody.cs +++ b/Content.Shared/GameObjects/Components/Body/IBody.cs @@ -56,6 +56,8 @@ namespace Content.Shared.GameObjects.Components.Body bool HasPart(string slot); + bool HasPart(IBodyPart part); + /// /// Removes the given reference, potentially /// dropping other BodyParts if they diff --git a/Content.Shared/GameObjects/Components/Body/Part/SharedBodyPartComponent.cs b/Content.Shared/GameObjects/Components/Body/Part/SharedBodyPartComponent.cs index b915e4112b..a7ddb84857 100644 --- a/Content.Shared/GameObjects/Components/Body/Part/SharedBodyPartComponent.cs +++ b/Content.Shared/GameObjects/Components/Body/Part/SharedBodyPartComponent.cs @@ -286,7 +286,8 @@ namespace Content.Shared.GameObjects.Components.Body.Part private void AddedToBody(IBody body) { - Owner.Transform.AttachParent(Body!.Owner); + Owner.Transform.LocalRotation = 0; + Owner.Transform.AttachParent(body.Owner); OnAddedToBody(body); foreach (var mechanism in _mechanisms) diff --git a/Content.Shared/GameObjects/Components/Body/SharedBodyComponent.cs b/Content.Shared/GameObjects/Components/Body/SharedBodyComponent.cs index 544d914c78..50232a7c0f 100644 --- a/Content.Shared/GameObjects/Components/Body/SharedBodyComponent.cs +++ b/Content.Shared/GameObjects/Components/Body/SharedBodyComponent.cs @@ -145,6 +145,13 @@ namespace Content.Shared.GameObjects.Components.Body return _parts.ContainsKey(slot); } + public bool HasPart(IBodyPart part) + { + DebugTools.AssertNotNull(part); + + return _parts.ContainsValue(part); + } + public void RemovePart(IBodyPart part) { DebugTools.AssertNotNull(part); diff --git a/Resources/Groups/groups.yml b/Resources/Groups/groups.yml index 360cbb95f5..7bc5bc55da 100644 --- a/Resources/Groups/groups.yml +++ b/Resources/Groups/groups.yml @@ -109,6 +109,7 @@ - deleteewi - hurt - toggledisallowlatejoin + - attachbodypart CanViewVar: true CanAdminPlace: true CanAdminMenu: true @@ -211,6 +212,7 @@ - deleteewi - hurt - toggledisallowlatejoin + - attachbodypart CanViewVar: true CanAdminPlace: true CanScript: true