diff --git a/Content.Client/GameObjects/Components/Actor/CharacterInterface.cs b/Content.Client/GameObjects/Components/Actor/CharacterInterface.cs index 41180e75d4..280f0e557e 100644 --- a/Content.Client/GameObjects/Components/Actor/CharacterInterface.cs +++ b/Content.Client/GameObjects/Components/Actor/CharacterInterface.cs @@ -97,6 +97,7 @@ namespace Content.Client.GameObjects.Components.Actor if (Window != null) { _gameHud.CharacterButtonVisible = false; + Window.Close(); } break; diff --git a/Content.Client/GameObjects/Components/HUD/Inventory/ClientInventoryComponent.cs b/Content.Client/GameObjects/Components/HUD/Inventory/ClientInventoryComponent.cs index 587c836d23..cc82853dfb 100644 --- a/Content.Client/GameObjects/Components/HUD/Inventory/ClientInventoryComponent.cs +++ b/Content.Client/GameObjects/Components/HUD/Inventory/ClientInventoryComponent.cs @@ -16,21 +16,32 @@ using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefine using static Content.Shared.GameObjects.SharedInventoryComponent.ClientInventoryMessage; using Content.Client.GameObjects.Components.Mobs; using Content.Client.GameObjects.Components.Actor; +using Content.Client.UserInterface; +using Robust.Client.GameObjects; +using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.Interfaces.Network; namespace Content.Client.GameObjects { /// /// A character UI which shows items the user has equipped within his inventory /// - public class ClientInventoryComponent : SharedInventoryComponent, ICharacterUI + public class ClientInventoryComponent : SharedInventoryComponent { - private Dictionary _slots = new Dictionary(); + private readonly Dictionary _slots = new Dictionary(); + + [Dependency] +#pragma warning disable 649 + private readonly IGameHud _gameHud; +#pragma warning restore 649 /// - /// Holds the godot control for the inventory window + /// Holds the godot control for the inventory window /// private InventoryWindow _window; + public SS14Window Window => _window; + private string _templateName = "HumanInventory"; //stored for serialization purposes /// @@ -59,11 +70,12 @@ namespace Content.Client.GameObjects var reflectionManager = IoCManager.Resolve(); var type = reflectionManager.LooseGetType(_templateName); DebugTools.Assert(type != null); - _inventory = (Inventory)Activator.CreateInstance(type); + _inventory = (Inventory) Activator.CreateInstance(type); //Creates godot control class for inventory _window = new InventoryWindow(this); _window.CreateInventory(_inventory); + _window.OnClose += () => _gameHud.InventoryButtonDown = false; if (Owner.TryGetComponent(out _sprite)) { @@ -73,6 +85,7 @@ namespace Content.Client.GameObjects { continue; } + _sprite.LayerMapReserveBlank(mask); } } @@ -102,8 +115,6 @@ namespace Content.Client.GameObjects var doneSlots = new HashSet(); - var entityManager = IoCManager.Resolve(); - foreach (var (slot, entityUid) in cast.Entities) { if (_slots.ContainsKey(slot)) @@ -112,7 +123,7 @@ namespace Content.Client.GameObjects _clearSlot(slot); } - var entity = entityManager.GetEntity(entityUid); + var entity = Owner.EntityManager.GetEntity(entityUid); _slots[slot] = entity; _setSlot(slot, entity); doneSlots.Add(slot); @@ -168,18 +179,59 @@ namespace Content.Client.GameObjects SendNetworkMessage(equipmessage); } + public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, + IComponent component = null) + { + base.HandleMessage(message, netChannel, component); + + switch (message) + { + case PlayerAttachedMsg _: + _gameHud.InventoryButtonVisible = true; + _gameHud.InventoryButtonToggled = b => + { + if (b) + { + Window.Open(); + } + else + { + Window.Close(); + } + }; + + break; + + case PlayerDetachedMsg _: + _gameHud.InventoryButtonVisible = false; + _window.Close(); + + break; + } + } + /// /// Temporary window to hold the basis for inventory hud /// - private class InventoryWindow : GridContainer + private sealed class InventoryWindow : SS14Window { private List IndexedSlots; - private Dictionary InventorySlots = new Dictionary(); //ordered dictionary? + + private Dictionary + InventorySlots = new Dictionary(); //ordered dictionary? + private readonly ClientInventoryComponent InventoryComponent; + private readonly GridContainer _gridContainer; public InventoryWindow(ClientInventoryComponent inventory) { + Title = "Inventory"; + InventoryComponent = inventory; + _gridContainer = new GridContainer(); + Contents.AddChild(_gridContainer); + + Size = CombinedMinimumSize; } /// @@ -187,7 +239,7 @@ namespace Content.Client.GameObjects /// public void CreateInventory(Inventory inventory) { - Columns = inventory.Columns; + _gridContainer.Columns = inventory.Columns; IndexedSlots = new List(inventory.SlotMasks); @@ -213,7 +265,7 @@ namespace Content.Client.GameObjects button.Text = button.ToolTip = SlotNames[slot]; } - AddChild(newButton); + _gridContainer.AddChild(newButton); } } @@ -255,7 +307,7 @@ namespace Content.Client.GameObjects private void RemoveFromInventory(BaseButton.ButtonEventArgs args) { args.Button.Pressed = false; - var control = (InventoryButton)args.Button.Parent; + var control = (InventoryButton) args.Button.Parent; InventoryComponent.SendUnequipMessage(control.Slot); } @@ -263,7 +315,7 @@ namespace Content.Client.GameObjects private void AddToInventory(BaseButton.ButtonEventArgs args) { args.Button.Pressed = false; - var control = (InventoryButton)args.Button.Parent; + var control = (InventoryButton) args.Button.Parent; InventoryComponent.SendEquipMessage(control.Slot); } diff --git a/Content.Client/GameObjects/EntitySystems/CharacterInterfaceSystem.cs b/Content.Client/GameObjects/EntitySystems/CharacterInterfaceSystem.cs index 76334986d0..9a061cf26a 100644 --- a/Content.Client/GameObjects/EntitySystems/CharacterInterfaceSystem.cs +++ b/Content.Client/GameObjects/EntitySystems/CharacterInterfaceSystem.cs @@ -23,10 +23,10 @@ namespace Content.Client.GameObjects.EntitySystems var inputSys = EntitySystemManager.GetEntitySystem(); inputSys.BindMap.BindFunction(ContentKeyFunctions.OpenCharacterMenu, - new PointerInputCmdHandler(HandleOpenCharacterMenu)); + InputCmdHandler.FromDelegate(s => HandleOpenCharacterMenu())); } - private void HandleOpenCharacterMenu(in PointerInputCmdHandler.PointerInputCmdArgs args) + private void HandleOpenCharacterMenu() { if (_playerManager.LocalPlayer.ControlledEntity == null || !_playerManager.LocalPlayer.ControlledEntity.TryGetComponent(out CharacterInterface characterInterface)) diff --git a/Content.Client/GameObjects/EntitySystems/ClientInventorySystem.cs b/Content.Client/GameObjects/EntitySystems/ClientInventorySystem.cs new file mode 100644 index 0000000000..34bdbb1f4d --- /dev/null +++ b/Content.Client/GameObjects/EntitySystems/ClientInventorySystem.cs @@ -0,0 +1,69 @@ +using Content.Client.UserInterface; +using Content.Shared.Input; +using Robust.Client.GameObjects.EntitySystems; +using Robust.Client.Player; +using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Input; +using Robust.Shared.IoC; + +namespace Content.Client.GameObjects.EntitySystems +{ + public sealed class ClientInventorySystem : EntitySystem + { +#pragma warning disable 649 + [Dependency] private readonly IGameHud _gameHud; + [Dependency] private readonly IPlayerManager _playerManager; +#pragma warning restore 649 + + public override void Initialize() + { + base.Initialize(); + + var inputSys = EntitySystemManager.GetEntitySystem(); + inputSys.BindMap.BindFunction(ContentKeyFunctions.OpenInventoryMenu, + InputCmdHandler.FromDelegate(s => HandleOpenInventoryMenu())); + } + + private void HandleOpenInventoryMenu() + { + if (_playerManager.LocalPlayer.ControlledEntity == null + || !_playerManager.LocalPlayer.ControlledEntity.TryGetComponent(out ClientInventoryComponent clientInventory)) + { + return; + } + + var menu = clientInventory.Window; + + if (menu.IsOpen) + { + if (menu.IsAtFront()) + { + _setOpenValue(menu, false); + } + else + { + menu.MoveToFront(); + } + } + else + { + _setOpenValue(menu, true); + } + } + + private void _setOpenValue(SS14Window menu, bool value) + { + if (value) + { + _gameHud.InventoryButtonDown = true; + menu.OpenCentered(); + } + else + { + _gameHud.InventoryButtonDown = false; + menu.Close(); + } + } + } +} diff --git a/Content.Client/Input/ContentContexts.cs b/Content.Client/Input/ContentContexts.cs index ced9f97555..d768e88611 100644 --- a/Content.Client/Input/ContentContexts.cs +++ b/Content.Client/Input/ContentContexts.cs @@ -26,6 +26,7 @@ namespace Content.Client.Input human.AddFunction(ContentKeyFunctions.ThrowItemInHand); human.AddFunction(ContentKeyFunctions.OpenContextMenu); human.AddFunction(ContentKeyFunctions.OpenCraftingMenu); + human.AddFunction(ContentKeyFunctions.OpenInventoryMenu); // Disabled until there is feedback, so hitting tab doesn't suddenly break interaction. // human.AddFunction(ContentKeyFunctions.ToggleCombatMode); diff --git a/Content.Client/UserInterface/GameHud.cs b/Content.Client/UserInterface/GameHud.cs index 7be4e207e1..ea5288dc6b 100644 --- a/Content.Client/UserInterface/GameHud.cs +++ b/Content.Client/UserInterface/GameHud.cs @@ -30,10 +30,17 @@ namespace Content.Client.UserInterface bool CharacterButtonVisible { get; set; } Action CharacterButtonToggled { get; set; } + // Inventory top button. + bool InventoryButtonDown { get; set; } + bool InventoryButtonVisible { get; set; } + Action InventoryButtonToggled { get; set; } + // Crafting top button. bool CraftingButtonDown { get; set; } bool CraftingButtonVisible { get; set; } Action CraftingButtonToggled { get; set; } + + // Sandbox top button. bool SandboxButtonDown { get; set; } bool SandboxButtonVisible { get; set; } Action SandboxButtonToggled { get; set; } @@ -48,13 +55,14 @@ namespace Content.Client.UserInterface private TopButton _buttonEscapeMenu; private TopButton _buttonTutorial; private TopButton _buttonCharacterMenu; + private TopButton _buttonInventoryMenu; private TopButton _buttonCraftingMenu; private TopButton _buttonSandboxMenu; private TutorialWindow _tutorialWindow; #pragma warning disable 649 [Dependency] private readonly IResourceCache _resourceCache; - [Dependency] private readonly ILocalizationManager _localizationManager; + [Dependency] private readonly ILocalizationManager _loc; [Dependency] private readonly IInputManager _inputManager; #pragma warning restore 649 @@ -66,6 +74,7 @@ namespace Content.Client.UserInterface var escapeTexture = _resourceCache.GetTexture("/Textures/UserInterface/hamburger.svg.96dpi.png"); var characterTexture = _resourceCache.GetTexture("/Textures/UserInterface/character.svg.96dpi.png"); + var inventoryTexture = _resourceCache.GetTexture("/Textures/UserInterface/inventory.svg.96dpi.png"); var craftingTexture = _resourceCache.GetTexture("/Textures/UserInterface/hammer.svg.96dpi.png"); var tutorialTexture = _resourceCache.GetTexture("/Textures/UserInterface/students-cap.svg.96dpi.png"); var sandboxTexture = _resourceCache.GetTexture("/Textures/UserInterface/sandbox.svg.96dpi.png"); @@ -82,7 +91,7 @@ namespace Content.Client.UserInterface // Escape _buttonEscapeMenu = new TopButton(escapeTexture, "Esc") { - ToolTip = _localizationManager.GetString("Open escape menu.") + ToolTip = _loc.GetString("Open escape menu.") }; _topButtonsContainer.AddChild(_buttonEscapeMenu); @@ -92,17 +101,17 @@ namespace Content.Client.UserInterface // Tutorial _buttonTutorial = new TopButton(tutorialTexture, "F1") { - ToolTip = _localizationManager.GetString("Open tutorial.") + ToolTip = _loc.GetString("Open tutorial.") }; _topButtonsContainer.AddChild(_buttonTutorial); _buttonTutorial.OnToggled += a => ButtonTutorialOnOnToggled(); - // Inventory + // Character _buttonCharacterMenu = new TopButton(characterTexture, "C") { - ToolTip = _localizationManager.GetString("Open character menu."), + ToolTip = _loc.GetString("Open character menu."), Visible = false }; @@ -110,10 +119,21 @@ namespace Content.Client.UserInterface _buttonCharacterMenu.OnToggled += args => CharacterButtonToggled?.Invoke(args.Pressed); + // Inventory + _buttonInventoryMenu = new TopButton(inventoryTexture, "I") + { + ToolTip = _loc.GetString("Open inventory menu."), + Visible = false + }; + + _topButtonsContainer.AddChild(_buttonInventoryMenu); + + _buttonInventoryMenu.OnToggled += args => InventoryButtonToggled?.Invoke(args.Pressed); + // Crafting _buttonCraftingMenu = new TopButton(craftingTexture, "G") { - ToolTip = _localizationManager.GetString("Open crafting menu."), + ToolTip = _loc.GetString("Open crafting menu."), Visible = false }; @@ -124,7 +144,7 @@ namespace Content.Client.UserInterface // Sandbox _buttonSandboxMenu = new TopButton(sandboxTexture, "B") { - ToolTip = _localizationManager.GetString("Open sandbox menu."), + ToolTip = _loc.GetString("Open sandbox menu."), Visible = true }; @@ -185,6 +205,20 @@ namespace Content.Client.UserInterface public Action CharacterButtonToggled { get; set; } + public bool InventoryButtonDown + { + get => _buttonInventoryMenu.Pressed; + set => _buttonInventoryMenu.Pressed = value; + } + + public bool InventoryButtonVisible + { + get => _buttonInventoryMenu.Visible; + set => _buttonInventoryMenu.Visible = value; + } + + public Action InventoryButtonToggled { get; set; } + public bool CraftingButtonDown { get => _buttonCraftingMenu.Pressed; diff --git a/Content.Shared/Input/ContentKeyFunctions.cs b/Content.Shared/Input/ContentKeyFunctions.cs index f80c0063aa..2b63091d36 100644 --- a/Content.Shared/Input/ContentKeyFunctions.cs +++ b/Content.Shared/Input/ContentKeyFunctions.cs @@ -5,18 +5,19 @@ namespace Content.Shared.Input [KeyFunctions] public static class ContentKeyFunctions { - public static readonly BoundKeyFunction SwapHands = "SwapHands"; - public static readonly BoundKeyFunction Drop = "Drop"; public static readonly BoundKeyFunction ActivateItemInHand = "ActivateItemInHand"; - public static readonly BoundKeyFunction OpenCharacterMenu = "OpenCharacterMenu"; - public static readonly BoundKeyFunction OpenCraftingMenu = "OpenCraftingMenu"; - public static readonly BoundKeyFunction ExamineEntity = "ExamineEntity"; - public static readonly BoundKeyFunction UseItemInHand = "UseItemInHand"; // use hand item on world entity public static readonly BoundKeyFunction ActivateItemInWorld = "ActivateItemInWorld"; // default action on world entity - public static readonly BoundKeyFunction ThrowItemInHand = "ThrowItemInHand"; - public static readonly BoundKeyFunction OpenContextMenu = "OpenContextMenu"; + public static readonly BoundKeyFunction Drop = "Drop"; + public static readonly BoundKeyFunction ExamineEntity = "ExamineEntity"; public static readonly BoundKeyFunction FocusChat = "FocusChatWindow"; - public static readonly BoundKeyFunction ToggleCombatMode = "ToggleCombatMode"; + public static readonly BoundKeyFunction OpenCharacterMenu = "OpenCharacterMenu"; + public static readonly BoundKeyFunction OpenContextMenu = "OpenContextMenu"; + public static readonly BoundKeyFunction OpenCraftingMenu = "OpenCraftingMenu"; + public static readonly BoundKeyFunction OpenInventoryMenu = "OpenInventoryMenu"; public static readonly BoundKeyFunction OpenTutorial = "OpenTutorial"; + public static readonly BoundKeyFunction SwapHands = "SwapHands"; + public static readonly BoundKeyFunction ThrowItemInHand = "ThrowItemInHand"; + public static readonly BoundKeyFunction ToggleCombatMode = "ToggleCombatMode"; + public static readonly BoundKeyFunction UseItemInHand = "UseItemInHand"; // use hand item on world entity } } diff --git a/Resources/keybinds.yml b/Resources/keybinds.yml index 46afc74b07..d21fbb314c 100644 --- a/Resources/keybinds.yml +++ b/Resources/keybinds.yml @@ -82,3 +82,6 @@ binds: - function: OpenTutorial type: state key: F1 +- function: OpenInventoryMenu + type: state + key: I