From 8eb96cfb0106f09e0fe44e99d48c9a16237358ff Mon Sep 17 00:00:00 2001 From: Leo Date: Fri, 8 Jan 2021 10:29:08 -0300 Subject: [PATCH] Add setoutfit command (#2874) * Add setoutfit command * Adds setoutfit as a verb and adds a proper UI to the command * Removes from AdminMenuWindow * Changes the SetOutfit verb to be a component verb instead of a global verb * Addresses reviews * Remove empty method * Remove on server aswell --- .../HUD/Inventory/ClientInventoryComponent.cs | 3 + .../AdminMenu/AdminMenuWindow.cs | 5 +- .../AdminMenu/SetOutfit/SetOutfitEui.cs | 42 +++++++ .../AdminMenu/SetOutfit/SetOutfitMenu.xaml | 17 +++ .../AdminMenu/SetOutfit/SetOutfitMenu.xaml.cs | 105 ++++++++++++++++++ .../Commands/SetOutfitCommand.cs | 85 ++++++++++++++ .../Components/GUI/InventoryComponent.cs | 62 +++++++++-- Content.Server/SetOutfitEui.cs | 55 +++++++++ .../Administration/SetOutfitEuiState.cs | 13 +++ 9 files changed, 378 insertions(+), 9 deletions(-) create mode 100644 Content.Client/UserInterface/AdminMenu/SetOutfit/SetOutfitEui.cs create mode 100644 Content.Client/UserInterface/AdminMenu/SetOutfit/SetOutfitMenu.xaml create mode 100644 Content.Client/UserInterface/AdminMenu/SetOutfit/SetOutfitMenu.xaml.cs create mode 100644 Content.Server/Administration/Commands/SetOutfitCommand.cs create mode 100644 Content.Server/SetOutfitEui.cs create mode 100644 Content.Shared/Administration/SetOutfitEuiState.cs diff --git a/Content.Client/GameObjects/Components/HUD/Inventory/ClientInventoryComponent.cs b/Content.Client/GameObjects/Components/HUD/Inventory/ClientInventoryComponent.cs index 97ff7454e5..d6458ee8f5 100644 --- a/Content.Client/GameObjects/Components/HUD/Inventory/ClientInventoryComponent.cs +++ b/Content.Client/GameObjects/Components/HUD/Inventory/ClientInventoryComponent.cs @@ -4,12 +4,15 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Client.GameObjects.Components.Clothing; using Content.Shared.GameObjects.Components.Inventory; +using Content.Shared.GameObjects.Verbs; using Content.Shared.Preferences.Appearance; +using Robust.Client.Console; using Robust.Client.GameObjects; using Robust.Client.Interfaces.GameObjects.Components; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.IoC; +using Robust.Shared.Localization; using Robust.Shared.ViewVariables; using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines; using static Content.Shared.GameObjects.Components.Inventory.SharedInventoryComponent.ClientInventoryMessage; diff --git a/Content.Client/UserInterface/AdminMenu/AdminMenuWindow.cs b/Content.Client/UserInterface/AdminMenu/AdminMenuWindow.cs index b4d9e4b7e6..e02483d5ee 100644 --- a/Content.Client/UserInterface/AdminMenu/AdminMenuWindow.cs +++ b/Content.Client/UserInterface/AdminMenu/AdminMenuWindow.cs @@ -1,10 +1,11 @@ -#nullable enable +#nullable enable using System; using System.Collections.Generic; using System.Linq; using Content.Client.GameObjects.EntitySystems; using Content.Client.StationEvents; using Content.Shared.Atmos; +using Content.Shared.Roles; using Robust.Client.Console; using Robust.Client.Graphics.Drawing; using Robust.Client.Interfaces.Placement; @@ -44,7 +45,7 @@ namespace Content.Client.UserInterface.AdminMenu { new SpawnEntitiesCommandButton(), new SpawnTilesCommandButton(), - new StationEventsCommandButton(), + new StationEventsCommandButton() }; private readonly List _debugButtons = new() { diff --git a/Content.Client/UserInterface/AdminMenu/SetOutfit/SetOutfitEui.cs b/Content.Client/UserInterface/AdminMenu/SetOutfit/SetOutfitEui.cs new file mode 100644 index 0000000000..0fa0229392 --- /dev/null +++ b/Content.Client/UserInterface/AdminMenu/SetOutfit/SetOutfitEui.cs @@ -0,0 +1,42 @@ +using Content.Client.Eui; +using Content.Shared.Eui; +using JetBrains.Annotations; +using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.IoC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Content.Shared.Administration; + +namespace Content.Client.UserInterface.AdminMenu.SetOutfit +{ + [UsedImplicitly] + public sealed class SetOutfitEui : BaseEui + { + private readonly SetOutfitMenu _window; + public SetOutfitEui() + { + _window = new SetOutfitMenu(); + } + + public override void Opened() + { + _window.OpenCentered(); + } + + public override void Closed() + { + base.Closed(); + _window.Close(); + } + + public override void HandleState(EuiStateBase state) + { + var outfitState = (SetOutfitEuiState) state; + _window.TargetEntityId = outfitState.TargetEntityId; + + } + } +} diff --git a/Content.Client/UserInterface/AdminMenu/SetOutfit/SetOutfitMenu.xaml b/Content.Client/UserInterface/AdminMenu/SetOutfit/SetOutfitMenu.xaml new file mode 100644 index 0000000000..ddec212682 --- /dev/null +++ b/Content.Client/UserInterface/AdminMenu/SetOutfit/SetOutfitMenu.xaml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/Content.Client/UserInterface/AdminMenu/SetOutfit/SetOutfitMenu.xaml.cs b/Content.Client/UserInterface/AdminMenu/SetOutfit/SetOutfitMenu.xaml.cs new file mode 100644 index 0000000000..0f1fba1fbf --- /dev/null +++ b/Content.Client/UserInterface/AdminMenu/SetOutfit/SetOutfitMenu.xaml.cs @@ -0,0 +1,105 @@ +#nullable enable +using System; +using Content.Shared.Construction; +using Content.Shared.Roles; +using Robust.Client.AutoGenerated; +using Robust.Client.Console; +using Robust.Client.Interfaces.ResourceManagement; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using Robust.Client.UserInterface.XAML; +using Robust.Client.Utility; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Maths; +using Robust.Shared.Prototypes; + +namespace Content.Client.UserInterface.AdminMenu.SetOutfit +{ + [GenerateTypedNameReferences] + public partial class SetOutfitMenu : SS14Window + { + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IClientConsole _console = default!; + + public EntityUid? TargetEntityId { get; set; } + protected override Vector2? CustomSize => (250, 320); + + private StartingGearPrototype? _selectedOutfit; + + public SetOutfitMenu() + { + IoCManager.InjectDependencies(this); + RobustXamlLoader.Load(this); + + Title = Loc.GetString("Set Outfit"); + + ConfirmButton.Text = Loc.GetString("Confirm"); + ConfirmButton.OnPressed += ConfirmButtonOnOnPressed; + SearchBar.OnTextChanged += SearchBarOnOnTextChanged; + OutfitList.OnItemSelected += OutfitListOnOnItemSelected; + OutfitList.OnItemDeselected += OutfitListOnOnItemDeselected; + PopulateList(); + } + + private void ConfirmButtonOnOnPressed(BaseButton.ButtonEventArgs obj) + { + if (TargetEntityId == null || _selectedOutfit == null) + return; + var command = $"setoutfit {TargetEntityId} {_selectedOutfit.ID}"; + _console.ProcessCommand(command); + Close(); + } + + private void OutfitListOnOnItemSelected(ItemList.ItemListSelectedEventArgs obj) + { + _selectedOutfit = (StartingGearPrototype) obj.ItemList[obj.ItemIndex].Metadata!; + ConfirmButton.Disabled = false; + } + + private void OutfitListOnOnItemDeselected(ItemList.ItemListDeselectedEventArgs obj) + { + _selectedOutfit = null; + ConfirmButton.Disabled = true; + } + + + private void SearchBarOnOnTextChanged(LineEdit.LineEditEventArgs obj) + { + PopulateByFilter(SearchBar.Text); + } + + private void PopulateList() + { + foreach (var gear in _prototypeManager.EnumeratePrototypes()) + { + OutfitList.Add(GetItem(gear, OutfitList)); + } + } + + private void PopulateByFilter(string filter) + { + OutfitList.Clear(); + foreach (var gear in _prototypeManager.EnumeratePrototypes()) + { + if (!string.IsNullOrEmpty(filter) && + gear.ID.ToLowerInvariant().Contains(filter.Trim().ToLowerInvariant())) + { + OutfitList.Add(GetItem(gear, OutfitList)); + } + } + } + + + private static ItemList.Item GetItem(StartingGearPrototype gear, ItemList itemList) + { + return new(itemList) + { + Metadata = gear, + Text = gear.ID + }; + } + } +} diff --git a/Content.Server/Administration/Commands/SetOutfitCommand.cs b/Content.Server/Administration/Commands/SetOutfitCommand.cs new file mode 100644 index 0000000000..980745f604 --- /dev/null +++ b/Content.Server/Administration/Commands/SetOutfitCommand.cs @@ -0,0 +1,85 @@ +using Content.Server.Eui; +using Content.Server.GameObjects.Components.GUI; +using Content.Server.GameObjects.Components.Items.Storage; +using Content.Shared.Administration; +using Content.Shared.Roles; +using Robust.Server.Interfaces.Console; +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.Prototypes; + +namespace Content.Server.Administration.Commands +{ + [AdminCommand(AdminFlags.Admin)] + class SetOutfitCommand : IClientCommand + { + public string Command => "setoutfit"; + + public string Description => Loc.GetString("Sets the outfit of the specified entity. The entity must have an InventoryComponent"); + + public string Help => Loc.GetString("Usage: {0} | {0} ", Command); + + public void Execute(IConsoleShell shell, IPlayerSession player, string[] args) + { + if (args.Length < 1) + { + shell.SendText(player, Loc.GetString("Wrong number of arguments.")); + return; + } + + if (!int.TryParse(args[0], out var entityUid)) + { + shell.SendText(player, Loc.GetString("EntityUid must be a number.")); + return; + } + + var entityManager = IoCManager.Resolve(); + + var eUid = new EntityUid(entityUid); + + if (!eUid.IsValid() || !entityManager.EntityExists(eUid)) + { + shell.SendText(player, Loc.GetString("Invalid entity ID.")); + return; + } + + var target = entityManager.GetEntity(eUid); + + if (!target.TryGetComponent(out var inventoryComponent)) + { + shell.SendText(player, Loc.GetString("Target entity does not have an inventory!")); + return; + } + + if (args.Length == 1) + { + var eui = IoCManager.Resolve(); + var ui = new SetOutfitEui(target); + eui.OpenEui(ui, player); + return; + } + + var prototypeManager = IoCManager.Resolve(); + if (!prototypeManager.TryIndex(args[1], out var startingGear)) + { + shell.SendText(player, Loc.GetString("Invalid outfit id")); + return; + } + + foreach (var slot in inventoryComponent.Slots) + { + inventoryComponent.ForceUnequip(slot); + var gearStr = startingGear.GetGear(slot, null); + if (gearStr != "") + { + var equipmentEntity = entityManager.SpawnEntity(gearStr, target.Transform.Coordinates); + inventoryComponent.Equip(slot, equipmentEntity.GetComponent()); + } + } + + } + } +} diff --git a/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs b/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs index 2ef74a187e..f6ea424c1f 100644 --- a/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs +++ b/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Content.Server.Administration.Commands; using Content.Server.GameObjects.Components.Items.Clothing; using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.EntitySystems.Click; @@ -9,8 +10,13 @@ using Content.Shared.GameObjects.Components.Inventory; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems.ActionBlocker; using Content.Shared.GameObjects.EntitySystems.EffectBlocker; +using Content.Shared.GameObjects.Verbs; using Content.Shared.Interfaces; +using Robust.Server.Console; using Robust.Server.GameObjects.Components.Container; +using Robust.Server.Interfaces.Console; +using Robust.Server.Interfaces.GameObjects; +using Robust.Server.Player; using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; @@ -32,8 +38,7 @@ namespace Content.Server.GameObjects.Components.GUI { [Dependency] private readonly IEntitySystemManager _entitySystemManager = default!; - [ViewVariables] - private readonly Dictionary _slotContainers = new(); + [ViewVariables] private readonly Dictionary _slotContainers = new(); private KeyValuePair? _hoverEntity; @@ -102,7 +107,7 @@ namespace Content.Server.GameObjects.Components.GUI bool IEffectBlocker.CanSlip() { - if(Owner.TryGetComponent(out InventoryComponent inventoryComponent) && + if (Owner.TryGetComponent(out InventoryComponent inventoryComponent) && inventoryComponent.TryGetSlotItem(EquipmentSlotDefines.Slots.SHOES, out ItemComponent shoes) ) { @@ -273,9 +278,11 @@ namespace Content.Server.GameObjects.Components.GUI return pass && _slotContainers[slot].CanInsert(item.Owner); } - public bool CanEquip(Slots slot, ItemComponent item, bool mobCheck = true) => CanEquip(slot, item, mobCheck, out var _); + public bool CanEquip(Slots slot, ItemComponent item, bool mobCheck = true) => + CanEquip(slot, item, mobCheck, out var _); - public bool CanEquip(Slots slot, IEntity entity, bool mobCheck = true) => CanEquip(slot, entity.GetComponent(), mobCheck); + public bool CanEquip(Slots slot, IEntity entity, bool mobCheck = true) => + CanEquip(slot, entity.GetComponent(), mobCheck); /// /// Drops the item in a slot. @@ -466,14 +473,15 @@ namespace Content.Server.GameObjects.Components.GUI { if (activeHand != null) { - await interactionSystem.Interaction(Owner, activeHand.Owner, itemContainedInSlot.Owner, - new EntityCoordinates()); + await interactionSystem.Interaction(Owner, activeHand.Owner, itemContainedInSlot.Owner, + new EntityCoordinates()); } else if (Unequip(msg.Inventoryslot)) { hands.PutInHand(itemContainedInSlot); } } + break; } case ClientInventoryUpdate.Hover: @@ -590,5 +598,45 @@ namespace Content.Server.GameObjects.Components.GUI return false; } + + [Verb] + private sealed class SetOutfitVerb : Verb + { + public override bool RequireInteractionRange => false; + public override bool BlockedByContainers => false; + + protected override void GetData(IEntity user, InventoryComponent component, VerbData data) + { + data.Visibility = VerbVisibility.Invisible; + if (!CanCommand(user)) + return; + + data.Visibility = VerbVisibility.Visible; + data.Text = Loc.GetString("Set Outfit"); + data.CategoryData = VerbCategories.Debug; + } + + protected override void Activate(IEntity user, InventoryComponent component) + { + if (!CanCommand(user)) + return; + + var target = component.Owner; + + var entityId = target.Uid.ToString(); + + var command = new SetOutfitCommand(); + var shell = IoCManager.Resolve(); + var args = new string[] {entityId}; + command.Execute(shell, user.PlayerSession(), args); + } + + private static bool CanCommand(IEntity user) + { + var groupController = IoCManager.Resolve(); + return user.TryGetComponent(out var player) && + groupController.CanCommand(player.playerSession, "setoutfit"); + } + } } } diff --git a/Content.Server/SetOutfitEui.cs b/Content.Server/SetOutfitEui.cs new file mode 100644 index 0000000000..3acbe72e23 --- /dev/null +++ b/Content.Server/SetOutfitEui.cs @@ -0,0 +1,55 @@ +using Content.Server.Administration; +using Content.Server.Database; +using Content.Server.Eui; +using Content.Shared.Administration; +using Content.Shared.Eui; +using JetBrains.Annotations; +using Robust.Server.Interfaces.Player; +using Robust.Shared.IoC; +using System; +using Robust.Shared.Interfaces.GameObjects; + +namespace Content.Server +{ + [UsedImplicitly] + public sealed class SetOutfitEui : BaseEui + { + [Dependency] private readonly IAdminManager _adminManager = default!; + private readonly IEntity _target; + public SetOutfitEui(IEntity entity) + { + _target = entity; + IoCManager.InjectDependencies(this); + } + + public override void Opened() + { + base.Opened(); + + StateDirty(); + _adminManager.OnPermsChanged += AdminManagerOnPermsChanged; + } + + public override EuiStateBase GetNewState() + { + return new SetOutfitEuiState + { + TargetEntityId = _target.Uid + }; + } + + private void AdminManagerOnPermsChanged(AdminPermsChangedEventArgs obj) + { + // Close UI if user loses +FUN. + if (obj.Player == Player && !UserAdminFlagCheck(AdminFlags.Fun)) + { + Close(); + } + } + private bool UserAdminFlagCheck(AdminFlags flags) + { + return _adminManager.HasAdminFlag(Player, flags); + } + + } +} diff --git a/Content.Shared/Administration/SetOutfitEuiState.cs b/Content.Shared/Administration/SetOutfitEuiState.cs new file mode 100644 index 0000000000..d16d55bb24 --- /dev/null +++ b/Content.Shared/Administration/SetOutfitEuiState.cs @@ -0,0 +1,13 @@ +using Content.Shared.Eui; +using Robust.Shared.Serialization; +using System; +using Robust.Shared.GameObjects; + +namespace Content.Shared.Administration +{ + [Serializable, NetSerializable] + public class SetOutfitEuiState : EuiStateBase + { + public EntityUid TargetEntityId; + } +}