Add changing the amount of hands on the GUI depending on your body parts (#1406)

* Multiple hands in gui first pass

* Remove IHandsComponent interface

* Create hand class and more hand textures

* Refactor ServerHandsComponent to use a single list of hands

* Seal SharedHand

* Fix picked up items not showing on top of the hand buttons

* Remove HandsGui buttons and panels dictionaries

* Fix items in hands rendering

* Fix wrong hand container comparison

* Fix not updating the location of duplicate hands

* Change ClientHandsComponent to use a SortedList instead of a dictionary

* More merge conflict fixes

* Change SortedList to List

* Fix hand button order

* Add item tooltip for more than 2 hands and updating when removing hands

* Add add hand and remove hand command

* Merge conflict fixes

* Remove nullable reference type from ContainerSlot

* Fix texture errors

* Fix error when reaching 0 hands

* Fix error when swapping hands with no hands

* Merged remove hand methods

* Fix item panel texture errors

* Merge conflict fixes

* Fix addhand and removehand command descriptions

* Add properly displaying tooltips for 2 hands

* Make hand indexes and locations consistent across the client and server

* Add dropping held entity if a hand is removed

* Change hand location to be calculated by index

* Made different hand gui updates more consistent

* Remove human body yml testing changes

* Sanitize addhand and removehand commands

* Merge conflict fixes

* Remove testing changes

* Revert body system changes

* Add missing imports

* Remove obsolete hands parameter in yml files

* Fix broken import

* Fix startup error and adding and removing hands on the same tick

* Make hand container id use an uint

In case someone gets more than 2 billion hands

* Rename hand component files

* Make hands state use an array
This commit is contained in:
DrSmugleaf
2020-07-25 15:11:16 +02:00
committed by GitHub
parent 3a4ad42c80
commit 4b4e83d2bf
74 changed files with 1106 additions and 558 deletions

View File

@@ -1,4 +1,5 @@
using Content.Shared.GameObjects; using Content.Client.GameObjects.Components.Items;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Inventory; using Content.Shared.GameObjects.Components.Inventory;
using Content.Shared.GameObjects.Components.Items; using Content.Shared.GameObjects.Components.Items;
using Robust.Client.Graphics; using Robust.Client.Graphics;

View File

@@ -1,215 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Content.Client.Interfaces.GameObjects;
using Content.Client.UserInterface;
using Content.Shared.GameObjects;
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.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects
{
[RegisterComponent]
[ComponentReference(typeof(IHandsComponent))]
public class HandsComponent : SharedHandsComponent, IHandsComponent
{
private HandsGui _gui;
#pragma warning disable 649
[Dependency] private readonly IGameHud _gameHud;
#pragma warning restore 649
[ViewVariables] private readonly Dictionary<string, IEntity> _hands = new Dictionary<string, IEntity>();
[ViewVariables] public string ActiveIndex { get; private set; }
[ViewVariables] private ISpriteComponent _sprite;
[ViewVariables] public IEntity ActiveHand => GetEntity(ActiveIndex);
public override void OnRemove()
{
base.OnRemove();
_gui?.Dispose();
}
public override void Initialize()
{
base.Initialize();
if (Owner.TryGetComponent(out _sprite))
{
foreach (var slot in _hands.Keys)
{
_sprite.LayerMapReserveBlank($"hand-{slot}");
_setHand(slot, _hands[slot]);
}
}
}
public IEntity GetEntity(string index)
{
if (!string.IsNullOrEmpty(index) && _hands.TryGetValue(index, out var entity))
{
return entity;
}
return null;
}
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
if (curState == null)
return;
var cast = (HandsComponentState) curState;
foreach (var (slot, uid) in cast.Hands)
{
IEntity entity = null;
try
{
entity = Owner.EntityManager.GetEntity(uid);
}
catch
{
// Nothing.
}
_hands[slot] = entity;
_setHand(slot, entity);
}
foreach (var slot in _hands.Keys.ToList())
{
if (!cast.Hands.ContainsKey(slot))
{
_hands[slot] = null;
_setHand(slot, null);
}
}
ActiveIndex = cast.ActiveIndex;
_gui?.UpdateHandIcons();
RefreshInHands();
}
private void _setHand(string hand, IEntity entity)
{
if (_sprite == null)
{
return;
}
if (entity == null)
{
_sprite.LayerSetVisible($"hand-{hand}", false);
return;
}
SetInHands(hand, entity);
}
private void SetInHands(string hand, IEntity entity)
{
if (entity == null)
{
_sprite.LayerSetVisible($"hand-{hand}", false);
return;
}
if (!entity.TryGetComponent(out ItemComponent item)) return;
var maybeInhands = item.GetInHandStateInfo(hand);
if (!maybeInhands.HasValue)
{
_sprite.LayerSetVisible($"hand-{hand}", false);
}
else
{
var (rsi, state) = maybeInhands.Value;
_sprite.LayerSetVisible($"hand-{hand}", true);
_sprite.LayerSetState($"hand-{hand}", state, rsi);
}
}
public void RefreshInHands()
{
if (!Initialized) return;
foreach (var (hand, entity) in _hands)
{
SetInHands(hand, entity);
}
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataReadWriteFunction(
"hands",
new List<string>(),
hands => hands.ForEach(slot => _hands.Add(slot, null)),
() => _hands.Keys.ToList());
serializer.DataField(this, x => ActiveIndex, "defaultHand", _hands.Keys.LastOrDefault());
}
public override void HandleMessage(ComponentMessage message, IComponent component)
{
base.HandleMessage(message, component);
switch (message)
{
case PlayerAttachedMsg _:
if (_gui == null)
{
_gui = new HandsGui();
}
else
{
_gui.Parent?.RemoveChild(_gui);
}
_gameHud.HandsContainer.AddChild(_gui);
_gui.UpdateHandIcons();
break;
case PlayerDetachedMsg _:
_gui.Parent?.RemoveChild(_gui);
break;
}
}
public void SendChangeHand(string index)
{
SendNetworkMessage(new ClientChangedHandMsg(index));
}
public void AttackByInHand(string index)
{
SendNetworkMessage(new ClientAttackByInHandMsg(index));
}
public void UseActiveHand()
{
if (GetEntity(ActiveIndex) != null)
{
SendNetworkMessage(new UseInHandMsg());
}
}
public void ActivateItemInHand(string handIndex)
{
if (GetEntity(handIndex) == null)
return;
SendNetworkMessage(new ActivateInHandMsg(handIndex));
}
}
}

View File

@@ -0,0 +1,256 @@
#nullable enable
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Client.UserInterface;
using Content.Shared.GameObjects.Components.Items;
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.ViewVariables;
namespace Content.Client.GameObjects.Components.Items
{
[RegisterComponent]
public class HandsComponent : SharedHandsComponent
{
private HandsGui? _gui;
#pragma warning disable 649
[Dependency] private readonly IGameHud _gameHud = default!;
#pragma warning restore 649
private readonly List<Hand> _hands = new List<Hand>();
[ViewVariables] public IReadOnlyList<Hand> Hands => _hands;
[ViewVariables] public string? ActiveIndex { get; private set; }
[ViewVariables] private ISpriteComponent? _sprite;
[ViewVariables] public IEntity? ActiveHand => GetEntity(ActiveIndex);
private void AddHand(Hand hand)
{
_hands.Insert(hand.Index, hand);
}
public Hand? GetHand(string? name)
{
return Hands.FirstOrDefault(hand => hand.Name == name);
}
private bool TryHand(string name, [MaybeNullWhen(false)] out Hand hand)
{
return (hand = GetHand(name)) != null;
}
public IEntity? GetEntity(string? handName)
{
if (handName == null)
{
return null;
}
return GetHand(handName)?.Entity;
}
public override void OnRemove()
{
base.OnRemove();
_gui?.Dispose();
}
public override void Initialize()
{
base.Initialize();
if (Owner.TryGetComponent(out _sprite))
{
foreach (var hand in _hands)
{
_sprite.LayerMapReserveBlank($"hand-{hand.Name}");
UpdateHandSprites(hand);
}
}
}
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
if (curState == null)
{
return;
}
var cast = (HandsComponentState) curState;
foreach (var sharedHand in cast.Hands)
{
if (!TryHand(sharedHand.Name, out var hand))
{
hand = new Hand(sharedHand, Owner.EntityManager);
AddHand(hand);
}
else
{
hand.Location = sharedHand.Location;
hand.Entity = sharedHand.EntityUid.HasValue
? Owner.EntityManager.GetEntity(sharedHand.EntityUid.Value)
: null;
}
UpdateHandSprites(hand);
}
foreach (var currentHand in _hands.ToList())
{
if (cast.Hands.All(newHand => newHand.Name != currentHand.Name))
{
_hands.Remove(currentHand);
_gui?.RemoveHand(currentHand);
HideHand(currentHand);
}
}
ActiveIndex = cast.ActiveIndex;
_gui?.UpdateHandIcons();
RefreshInHands();
}
private void HideHand(Hand hand)
{
_sprite?.LayerSetVisible($"hand-{hand.Name}", false);
}
private void UpdateHandSprites(Hand hand)
{
if (_sprite == null)
{
return;
}
var entity = hand.Entity;
var name = hand.Name;
if (entity == null)
{
_sprite.LayerSetVisible($"hand-{name}", false);
return;
}
if (!entity.TryGetComponent(out ItemComponent item)) return;
var maybeInHands = item.GetInHandStateInfo(name);
if (!maybeInHands.HasValue)
{
_sprite.LayerSetVisible($"hand-{name}", false);
}
else
{
var (rsi, state) = maybeInHands.Value;
_sprite.LayerSetVisible($"hand-{name}", true);
_sprite.LayerSetState($"hand-{name}", state, rsi);
}
}
public void RefreshInHands()
{
if (!Initialized) return;
foreach (var hand in _hands)
{
UpdateHandSprites(hand);
}
}
protected override void Startup()
{
ActiveIndex = _hands.LastOrDefault()?.Name;
}
public override void HandleMessage(ComponentMessage message, IComponent? component)
{
base.HandleMessage(message, component);
switch (message)
{
case PlayerAttachedMsg _:
if (_gui == null)
{
_gui = new HandsGui();
}
else
{
_gui.Parent?.RemoveChild(_gui);
}
_gameHud.HandsContainer.AddChild(_gui);
_gui.UpdateHandIcons();
break;
case PlayerDetachedMsg _:
_gui?.Parent?.RemoveChild(_gui);
break;
}
}
public void SendChangeHand(string index)
{
SendNetworkMessage(new ClientChangedHandMsg(index));
}
public void AttackByInHand(string index)
{
SendNetworkMessage(new ClientAttackByInHandMsg(index));
}
public void UseActiveHand()
{
if (GetEntity(ActiveIndex) != null)
{
SendNetworkMessage(new UseInHandMsg());
}
}
public void ActivateItemInHand(string handIndex)
{
if (GetEntity(handIndex) == null)
{
return;
}
SendNetworkMessage(new ActivateInHandMsg(handIndex));
}
}
public class Hand
{
// TODO: Separate into server hand and client hand
public Hand(SharedHand hand, IEntityManager manager, HandButton? button = null)
{
Index = hand.Index;
Name = hand.Name;
Location = hand.Location;
Button = button;
if (!hand.EntityUid.HasValue)
{
return;
}
manager.TryGetEntity(hand.EntityUid.Value, out var entity);
Entity = entity;
}
public int Index { get; }
public string Name { get; }
public HandLocation Location { get; set; }
public IEntity? Entity { get; set; }
public HandButton? Button { get; set; }
}
}

View File

@@ -14,7 +14,7 @@ using Robust.Shared.Serialization;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects namespace Content.Client.GameObjects.Components.Items
{ {
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(IItemComponent))] [ComponentReference(typeof(IItemComponent))]

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Content.Client.GameObjects.Components.Items;
using Content.Shared.GameObjects.Components.Storage; using Content.Shared.GameObjects.Components.Storage;
using Content.Client.Interfaces.GameObjects;
using Content.Client.Interfaces.GameObjects.Components.Interaction; using Content.Client.Interfaces.GameObjects.Components.Interaction;
using Robust.Client.Graphics.Drawing; using Robust.Client.Graphics.Drawing;
using Robust.Client.Interfaces.GameObjects.Components; using Robust.Client.Interfaces.GameObjects.Components;
@@ -137,7 +137,7 @@ namespace Content.Client.GameObjects.Components.Storage
{ {
var controlledEntity = IoCManager.Resolve<IPlayerManager>().LocalPlayer.ControlledEntity; var controlledEntity = IoCManager.Resolve<IPlayerManager>().LocalPlayer.ControlledEntity;
if (controlledEntity.TryGetComponent(out IHandsComponent hands)) if (controlledEntity.TryGetComponent(out HandsComponent hands))
{ {
StorageEntity.SendNetworkMessage(new InsertEntityMessage()); StorageEntity.SendNetworkMessage(new InsertEntityMessage());
} }
@@ -250,7 +250,7 @@ namespace Content.Client.GameObjects.Components.Storage
{ {
var controlledEntity = IoCManager.Resolve<IPlayerManager>().LocalPlayer.ControlledEntity; var controlledEntity = IoCManager.Resolve<IPlayerManager>().LocalPlayer.ControlledEntity;
if (controlledEntity.TryGetComponent(out IHandsComponent hands)) if (controlledEntity.TryGetComponent(out HandsComponent hands))
{ {
StorageEntity.SendNetworkMessage(new InsertEntityMessage()); StorageEntity.SendNetworkMessage(new InsertEntityMessage());
} }

View File

@@ -1,6 +1,6 @@
using System; using System;
using Content.Client.GameObjects.Components.Items;
using Content.Client.GameObjects.Components.Weapons.Ranged; using Content.Client.GameObjects.Components.Weapons.Ranged;
using Content.Client.Interfaces.GameObjects;
using Content.Shared.GameObjects.Components.Weapons.Ranged; using Content.Shared.GameObjects.Components.Weapons.Ranged;
using Robust.Client.GameObjects.EntitySystems; using Robust.Client.GameObjects.EntitySystems;
using Robust.Client.Interfaces.Graphics.ClientEye; using Robust.Client.Interfaces.Graphics.ClientEye;
@@ -11,7 +11,6 @@ using Robust.Shared.Input;
using Robust.Shared.Interfaces.Map; using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Timing; using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Log;
namespace Content.Client.GameObjects.EntitySystems namespace Content.Client.GameObjects.EntitySystems
{ {
@@ -48,7 +47,7 @@ namespace Content.Client.GameObjects.EntitySystems
{ {
return; return;
} }
var state = _inputSystem.CmdStates.GetState(EngineKeyFunctions.Use); var state = _inputSystem.CmdStates.GetState(EngineKeyFunctions.Use);
if (!_combatModeSystem.IsInCombatMode() || state != BoundKeyState.Down) if (!_combatModeSystem.IsInCombatMode() || state != BoundKeyState.Down)
{ {
@@ -58,7 +57,7 @@ namespace Content.Client.GameObjects.EntitySystems
} }
var entity = _playerManager.LocalPlayer.ControlledEntity; var entity = _playerManager.LocalPlayer.ControlledEntity;
if (entity == null || !entity.TryGetComponent(out IHandsComponent hands)) if (entity == null || !entity.TryGetComponent(out HandsComponent hands))
{ {
return; return;
} }

View File

@@ -1,19 +0,0 @@
using Robust.Shared.Interfaces.GameObjects;
namespace Content.Client.Interfaces.GameObjects
{
// HYPER SIMPLE HANDS API CLIENT SIDE.
// To allow for showing the HUD, mostly.
public interface IHandsComponent
{
IEntity GetEntity(string index);
string ActiveIndex { get; }
IEntity ActiveHand { get; }
void SendChangeHand(string index);
void AttackByInHand(string index);
void UseActiveHand();
void ActivateItemInHand(string handIndex);
void RefreshInHands();
}
}

View File

@@ -0,0 +1,15 @@
using Content.Shared.GameObjects.Components.Items;
using Robust.Client.Graphics;
namespace Content.Client.UserInterface
{
public class HandButton : ItemSlotButton
{
public HandButton(Texture texture, Texture storageTexture, HandLocation location) : base(texture, storageTexture)
{
Location = location;
}
public HandLocation Location { get; }
}
}

View File

@@ -1,14 +1,15 @@
using Content.Client.GameObjects; using System;
using Content.Client.Interfaces.GameObjects; using System.Linq;
using Content.Client.GameObjects.Components.Items;
using Content.Client.Utility; using Content.Client.Utility;
using Content.Shared.GameObjects.Components.Items;
using Content.Shared.Input; using Content.Shared.Input;
using Robust.Client.Interfaces.GameObjects.Components; using Robust.Client.Graphics;
using Robust.Client.Interfaces.ResourceManagement; using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Player; using Robust.Client.Player;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input; using Robust.Shared.Input;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Timing; using Robust.Shared.Timing;
@@ -16,67 +17,140 @@ namespace Content.Client.UserInterface
{ {
public class HandsGui : Control public class HandsGui : Control
{ {
private const string HandNameLeft = "left";
private const string HandNameRight = "right";
#pragma warning disable 0649 #pragma warning disable 0649
[Dependency] private readonly IPlayerManager _playerManager; [Dependency] private readonly IPlayerManager _playerManager;
[Dependency] private readonly IResourceCache _resourceCache; [Dependency] private readonly IResourceCache _resourceCache;
[Dependency] private readonly IItemSlotManager _itemSlotManager; [Dependency] private readonly IItemSlotManager _itemSlotManager;
#pragma warning restore 0649 #pragma warning restore 0649
private IEntity _leftHand; private readonly TextureRect _activeHandRect;
private IEntity _rightHand;
private readonly TextureRect ActiveHandRect; private readonly Texture _leftHandTexture;
private readonly Texture _middleHandTexture;
private readonly Texture _rightHandTexture;
private readonly ItemSlotButton _leftButton; private readonly ItemStatusPanel _leftPanel;
private readonly ItemSlotButton _rightButton; private readonly ItemStatusPanel _topPanel;
private readonly ItemStatusPanel _rightPanel;
private readonly ItemStatusPanel _rightStatusPanel; private readonly HBoxContainer _guiContainer;
private readonly ItemStatusPanel _leftStatusPanel; private readonly VBoxContainer _handsColumn;
private readonly HBoxContainer _handsContainer;
private int _lastHands;
public HandsGui() public HandsGui()
{ {
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);
var textureHandLeft = _resourceCache.GetTexture("/Textures/Interface/Inventory/hand_l.png"); AddChild(_guiContainer = new HBoxContainer
var textureHandRight = _resourceCache.GetTexture("/Textures/Interface/Inventory/hand_r.png");
var textureHandActive = _resourceCache.GetTexture("/Textures/Interface/Inventory/hand_active.png");
var storageTexture = _resourceCache.GetTexture("/Textures/Interface/Inventory/back.png");
_rightStatusPanel = new ItemStatusPanel(true);
_leftStatusPanel = new ItemStatusPanel(false);
_leftButton = new ItemSlotButton(textureHandLeft, storageTexture);
_rightButton = new ItemSlotButton(textureHandRight, storageTexture);
var hBox = new HBoxContainer
{ {
SeparationOverride = 0, SeparationOverride = 0,
Children = {_rightStatusPanel, _rightButton, _leftButton, _leftStatusPanel} Children =
}; {
(_rightPanel = ItemStatusPanel.FromSide(HandLocation.Right)),
(_handsColumn = new VBoxContainer
{
Children =
{
(_topPanel = ItemStatusPanel.FromSide(HandLocation.Middle)),
(_handsContainer = new HBoxContainer {SeparationOverride = 0})
}
}),
(_leftPanel = ItemStatusPanel.FromSide(HandLocation.Left))
}
});
AddChild(hBox); var textureHandActive = _resourceCache.GetTexture("/Textures/Interface/Inventory/hand_active.png");
_leftButton.OnPressed += args => HandKeyBindDown(args, HandNameLeft);
_leftButton.OnStoragePressed += args => _OnStoragePressed(args, HandNameLeft);
_rightButton.OnPressed += args => HandKeyBindDown(args, HandNameRight);
_rightButton.OnStoragePressed += args => _OnStoragePressed(args, HandNameRight);
// Active hand // Active hand
_leftButton.AddChild(ActiveHandRect = new TextureRect _activeHandRect = new TextureRect
{ {
Texture = textureHandActive, Texture = textureHandActive,
TextureScale = (2, 2) TextureScale = (2, 2)
}); };
_leftHandTexture = _resourceCache.GetTexture("/Textures/Interface/Inventory/hand_l.png");
_middleHandTexture = _resourceCache.GetTexture("/Textures/Interface/Inventory/hand_middle.png");
_rightHandTexture = _resourceCache.GetTexture("/Textures/Interface/Inventory/hand_r.png");
}
private ItemStatusPanel GetItemPanel(Hand hand)
{
return hand.Location switch
{
HandLocation.Left => _rightPanel,
HandLocation.Middle => _topPanel,
HandLocation.Right => _leftPanel,
_ => throw new IndexOutOfRangeException()
};
}
private Texture HandTexture(HandLocation location)
{
switch (location)
{
case HandLocation.Left:
return _leftHandTexture;
case HandLocation.Middle:
return _middleHandTexture;
case HandLocation.Right:
return _rightHandTexture;
default:
throw new ArgumentOutOfRangeException(nameof(location), location, null);
}
} }
/// <summary> /// <summary>
/// Gets the hands component controling this gui, returns true if successful and false if failure /// Adds a new hand to this control
/// </summary>
/// <param name="hand">The hand to add to this control</param>
/// <param name="buttonLocation">
/// The actual location of the button. The right hand is drawn
/// on the LEFT of the screen.
/// </param>
private void AddHand(Hand hand, HandLocation buttonLocation)
{
var buttonTexture = HandTexture(buttonLocation);
var storageTexture = _resourceCache.GetTexture("/Textures/Interface/Inventory/back.png");
var button = new HandButton(buttonTexture, storageTexture, buttonLocation);
var slot = hand.Name;
button.OnPressed += args => HandKeyBindDown(args, slot);
button.OnStoragePressed += args => _OnStoragePressed(args, slot);
_handsContainer.AddChild(button);
if (_activeHandRect.Parent == null)
{
button.AddChild(_activeHandRect);
_activeHandRect.SetPositionInParent(1);
}
hand.Button = button;
}
public void RemoveHand(Hand hand)
{
var button = hand.Button;
if (button != null)
{
if (button.Children.Contains(_activeHandRect))
{
button.RemoveChild(_activeHandRect);
}
_handsContainer.RemoveChild(button);
}
}
/// <summary>
/// Gets the hands component controlling this gui
/// </summary> /// </summary>
/// <param name="hands"></param> /// <param name="hands"></param>
/// <returns></returns> /// <returns>true if successful and false if failure</returns>
private bool TryGetHands(out IHandsComponent hands) private bool TryGetHands(out HandsComponent hands)
{ {
hands = default; hands = default;
@@ -93,50 +167,63 @@ namespace Content.Client.UserInterface
UpdateDraw(); UpdateDraw();
if (!TryGetHands(out var hands)) if (!TryGetHands(out var component))
{
return; return;
var left = hands.GetEntity(HandNameLeft);
var right = hands.GetEntity(HandNameRight);
ActiveHandRect.Parent.RemoveChild(ActiveHandRect);
var parent = hands.ActiveIndex == HandNameLeft ? _leftButton : _rightButton;
parent.AddChild(ActiveHandRect);
ActiveHandRect.SetPositionInParent(1);
if (left != _leftHand)
{
_leftHand = left;
_itemSlotManager.SetItemSlot(_leftButton, _leftHand);
} }
if (right != _rightHand) // TODO: Remove button on remove hand
var hands = component.Hands.OrderByDescending(x => x.Location).ToArray();
for (var i = 0; i < hands.Length; i++)
{ {
_rightHand = right; var hand = hands[i];
_itemSlotManager.SetItemSlot(_rightButton, _rightHand);
if (hand.Button == null)
{
AddHand(hand, hand.Location);
}
hand.Button!.Button.Texture = HandTexture(hand.Location);
hand.Button!.SetPositionInParent(i);
_itemSlotManager.SetItemSlot(hand.Button, hand.Entity);
} }
_activeHandRect.Parent?.RemoveChild(_activeHandRect);
component.GetHand(component.ActiveIndex)?.Button?.AddChild(_activeHandRect);
if (hands.Length > 0)
{
_activeHandRect.SetPositionInParent(1);
}
_leftPanel.SetPositionFirst();
_rightPanel.SetPositionLast();
} }
private void HandKeyBindDown(GUIBoundKeyEventArgs args, string handIndex) private void HandKeyBindDown(GUIBoundKeyEventArgs args, string slotName)
{ {
if (!TryGetHands(out var hands)) if (!TryGetHands(out var hands))
{
return; return;
}
if (args.Function == ContentKeyFunctions.MouseMiddle) if (args.Function == ContentKeyFunctions.MouseMiddle)
{ {
hands.SendChangeHand(handIndex); hands.SendChangeHand(slotName);
args.Handle(); args.Handle();
return; return;
} }
var entity = hands.GetEntity(handIndex); var entity = hands.GetEntity(slotName);
if (entity == null) if (entity == null)
{ {
if (args.Function == EngineKeyFunctions.UIClick && hands.ActiveIndex != handIndex) if (args.Function == EngineKeyFunctions.UIClick && hands.ActiveIndex != slotName)
{ {
hands.SendChangeHand(handIndex); hands.SendChangeHand(slotName);
args.Handle(); args.Handle();
} }
return; return;
} }
@@ -148,37 +235,131 @@ namespace Content.Client.UserInterface
if (args.Function == EngineKeyFunctions.UIClick) if (args.Function == EngineKeyFunctions.UIClick)
{ {
if (hands.ActiveIndex == handIndex) if (hands.ActiveIndex == slotName)
{ {
hands.UseActiveHand(); hands.UseActiveHand();
} }
else else
{ {
hands.AttackByInHand(handIndex); hands.AttackByInHand(slotName);
} }
args.Handle(); args.Handle();
return;
} }
} }
private void _OnStoragePressed(GUIBoundKeyEventArgs args, string handIndex) private void _OnStoragePressed(GUIBoundKeyEventArgs args, string handIndex)
{ {
if (args.Function != EngineKeyFunctions.UIClick) if (args.Function != EngineKeyFunctions.UIClick || !TryGetHands(out var hands))
return; {
if (!TryGetHands(out var hands))
return; return;
}
hands.ActivateItemInHand(handIndex); hands.ActivateItemInHand(handIndex);
} }
private void UpdatePanels()
{
if (!TryGetHands(out var component))
{
return;
}
foreach (var hand in component.Hands)
{
_itemSlotManager.UpdateCooldown(hand.Button, hand.Entity);
}
switch (component.Hands.Count)
{
case var n when n == 0 && _lastHands != 0:
_guiContainer.Visible = false;
_topPanel.Update(null);
_leftPanel.Update(null);
_rightPanel.Update(null);
break;
case 1:
if (_lastHands != 1)
{
_guiContainer.Visible = true;
_topPanel.Update(null);
_topPanel.Visible = false;
_leftPanel.Update(null);
_leftPanel.Visible = false;
_rightPanel.Visible = true;
if (!_guiContainer.Children.Contains(_rightPanel))
{
_rightPanel.AddChild(_rightPanel);
_rightPanel.SetPositionFirst();
}
}
_rightPanel.Update(component.Hands[0].Entity);
break;
case 2:
if (_lastHands != 2)
{
_guiContainer.Visible = true;
_topPanel.Update(null);
_topPanel.Visible = false;
_leftPanel.Visible = true;
_rightPanel.Visible = true;
if (_handsColumn.Children.Contains(_topPanel))
{
_handsColumn.RemoveChild(_topPanel);
}
}
_leftPanel.Update(component.Hands[0].Entity);
_rightPanel.Update(component.Hands[1].Entity);
// Order is left, right
foreach (var hand in component.Hands)
{
var tooltip = GetItemPanel(hand);
tooltip.Update(hand.Entity);
}
break;
case var n when n > 2:
if (_lastHands <= 2)
{
_guiContainer.Visible = true;
_topPanel.Visible = true;
_leftPanel.Visible = false;
_rightPanel.Visible = false;
if (!_handsColumn.Children.Contains(_topPanel))
{
_handsColumn.AddChild(_topPanel);
_topPanel.SetPositionFirst();
}
}
_topPanel.Update(component.ActiveHand);
_leftPanel.Update(null);
_rightPanel.Update(null);
break;
}
_lastHands = component.Hands.Count;
}
protected override void FrameUpdate(FrameEventArgs args) protected override void FrameUpdate(FrameEventArgs args)
{ {
base.FrameUpdate(args); base.FrameUpdate(args);
UpdatePanels();
_itemSlotManager.UpdateCooldown(_leftButton, _leftHand);
_itemSlotManager.UpdateCooldown(_rightButton, _rightHand);
_rightStatusPanel.Update(_rightHand);
_leftStatusPanel.Update(_leftHand);
} }
} }
} }

View File

@@ -1,15 +1,14 @@
using System; using System;
using Content.Client.UserInterface; using Content.Shared.GameObjects.Components.Items;
using Content.Shared.Input;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input; using Robust.Shared.Input;
using Robust.Shared.Maths; using Robust.Shared.Maths;
namespace Content.Client.GameObjects namespace Content.Client.UserInterface
{ {
public sealed class ItemSlotButton : MarginContainer public class ItemSlotButton : MarginContainer
{ {
public TextureRect Button { get; } public TextureRect Button { get; }
public SpriteView SpriteView { get; } public SpriteView SpriteView { get; }

View File

@@ -1,7 +1,11 @@
#nullable enable
using System;
using System.Collections.Generic; using System.Collections.Generic;
using Content.Client.GameObjects.Components; using Content.Client.GameObjects.Components;
using Content.Client.UserInterface.Stylesheets; using Content.Client.UserInterface.Stylesheets;
using Content.Client.Utility; using Content.Client.Utility;
using Content.Shared.GameObjects.Components.Items;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Drawing; using Robust.Client.Graphics.Drawing;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
@@ -26,22 +30,17 @@ namespace Content.Client.UserInterface
private readonly PanelContainer _panel; private readonly PanelContainer _panel;
[ViewVariables] [ViewVariables]
private IEntity _entity; private IEntity? _entity;
public ItemStatusPanel(bool isRightHand) public ItemStatusPanel(Texture texture, StyleBox.Margin margin)
{ {
// isRightHand means on the LEFT of the screen.
// Keep that in mind.
var panel = new StyleBoxTexture var panel = new StyleBoxTexture
{ {
Texture = ResC.GetTexture(isRightHand Texture = texture
? "/Textures/Interface/Nano/item_status_right.svg.96dpi.png"
: "/Textures/Interface/Nano/item_status_left.svg.96dpi.png")
}; };
panel.SetContentMarginOverride(StyleBox.Margin.Vertical, 4); panel.SetContentMarginOverride(StyleBox.Margin.Vertical, 4);
panel.SetContentMarginOverride(StyleBox.Margin.Horizontal, 6); panel.SetContentMarginOverride(StyleBox.Margin.Horizontal, 6);
panel.SetPatchMargin((isRightHand ? StyleBox.Margin.Left : StyleBox.Margin.Right) | StyleBox.Margin.Top, panel.SetPatchMargin(margin, 13);
13);
AddChild(_panel = new PanelContainer AddChild(_panel = new PanelContainer
{ {
@@ -67,7 +66,42 @@ namespace Content.Client.UserInterface
SizeFlagsVertical = SizeFlags.ShrinkEnd; SizeFlagsVertical = SizeFlags.ShrinkEnd;
} }
public void Update(IEntity entity) /// <summary>
/// Creates a new instance of <see cref="ItemStatusPanel"/>
/// based on whether or not it is being created for the right
/// or left hand.
/// </summary>
/// <param name="location">
/// The location of the hand that this panel is for
/// </param>
/// <returns>the new <see cref="ItemStatusPanel"/> instance</returns>
public static ItemStatusPanel FromSide(HandLocation location)
{
string texture;
StyleBox.Margin margin;
switch (location)
{
case HandLocation.Left:
texture = "/Textures/Interface/Nano/item_status_right.svg.96dpi.png";
margin = StyleBox.Margin.Left | StyleBox.Margin.Top;
break;
case HandLocation.Middle:
texture = "/Textures/Interface/Nano/item_status_left.svg.96dpi.png";
margin = StyleBox.Margin.Right | StyleBox.Margin.Top;
break;
case HandLocation.Right:
texture = "/Textures/Interface/Nano/item_status_left.svg.96dpi.png";
margin = StyleBox.Margin.Right | StyleBox.Margin.Top;
break;
default:
throw new ArgumentOutOfRangeException(nameof(location), location, null);
}
return new ItemStatusPanel(ResC.GetTexture(texture), margin);
}
public void Update(IEntity? entity)
{ {
if (entity == null) if (entity == null)
{ {
@@ -105,7 +139,7 @@ namespace Content.Client.UserInterface
ClearOldStatus(); ClearOldStatus();
foreach (var statusComponent in _entity.GetAllComponents<IItemStatus>()) foreach (var statusComponent in _entity!.GetAllComponents<IItemStatus>())
{ {
var control = statusComponent.MakeControl(); var control = statusComponent.MakeControl();
_statusContents.AddChild(control); _statusContents.AddChild(control);
@@ -114,9 +148,10 @@ namespace Content.Client.UserInterface
} }
} }
// TODO: Depending on if its a two-hand panel or not
protected override Vector2 CalculateMinimumSize() protected override Vector2 CalculateMinimumSize()
{ {
return Vector2.ComponentMax(base.CalculateMinimumSize(), (150, 00)); return Vector2.ComponentMax(base.CalculateMinimumSize(), (150, 0));
} }
} }
} }

View File

@@ -1,4 +1,5 @@
using Content.Server.GameObjects; using Content.Server.GameObjects;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Weapon.Melee; using Content.Server.GameObjects.Components.Weapon.Melee;
using Content.Server.GameObjects.EntitySystems; using Content.Server.GameObjects.EntitySystems;

View File

@@ -1,4 +1,5 @@
using Content.Server.GameObjects; using Content.Server.GameObjects;
using Content.Server.GameObjects.Components.GUI;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Log; using Robust.Shared.Log;
@@ -21,7 +22,8 @@ namespace Content.Server.AI.Operators.Inventory
/// <returns></returns> /// <returns></returns>
public override Outcome Execute(float frameTime) public override Outcome Execute(float frameTime)
{ {
if (!_owner.TryGetComponent(out HandsComponent handsComponent) || handsComponent.FindHand(_entity) == null) if (!_owner.TryGetComponent(out HandsComponent handsComponent) ||
!handsComponent.TryHand(_entity, out _))
{ {
return Outcome.Failed; return Outcome.Failed;
} }

View File

@@ -1,4 +1,5 @@
using Content.Server.GameObjects; using Content.Server.GameObjects;
using Content.Server.GameObjects.Components.GUI;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
namespace Content.Server.AI.Operators.Inventory namespace Content.Server.AI.Operators.Inventory

View File

@@ -1,4 +1,5 @@
using Content.Server.GameObjects; using Content.Server.GameObjects;
using Content.Server.GameObjects.Components.GUI;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
namespace Content.Server.AI.Operators.Inventory namespace Content.Server.AI.Operators.Inventory
@@ -22,9 +23,9 @@ namespace Content.Server.AI.Operators.Inventory
// TODO: If in clothing then click on it // TODO: If in clothing then click on it
foreach (var hand in handsComponent.ActivePriorityEnumerable()) foreach (var hand in handsComponent.ActivePriorityEnumerable())
{ {
if (handsComponent.GetHand(hand)?.Owner == _entity) if (handsComponent.GetItem(hand)?.Owner == _entity)
{ {
handsComponent.ActiveIndex = hand; handsComponent.ActiveHand = hand;
return Outcome.Success; return Outcome.Success;
} }
} }

View File

@@ -1,5 +1,5 @@
using Content.Server.GameObjects;
using Content.Server.GameObjects.Components; using Content.Server.GameObjects.Components;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.EntitySystems.Click; using Content.Server.GameObjects.EntitySystems.Click;
using Content.Server.Utility; using Content.Server.Utility;
using Robust.Shared.Containers; using Robust.Shared.Containers;
@@ -41,11 +41,11 @@ namespace Content.Server.AI.Operators.Inventory
foreach (var hand in handsComponent.ActivePriorityEnumerable()) foreach (var hand in handsComponent.ActivePriorityEnumerable())
{ {
if (handsComponent.GetHand(hand) == null) if (handsComponent.GetItem(hand) == null)
{ {
if (handsComponent.ActiveIndex != hand) if (handsComponent.ActiveHand != hand)
{ {
handsComponent.ActiveIndex = hand; handsComponent.ActiveHand = hand;
} }
emptyHands = true; emptyHands = true;

View File

@@ -1,5 +1,5 @@
using Content.Server.GameObjects;
using Content.Server.GameObjects.Components; using Content.Server.GameObjects.Components;
using Content.Server.GameObjects.Components.GUI;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
namespace Content.Server.AI.Operators.Inventory namespace Content.Server.AI.Operators.Inventory
@@ -38,8 +38,8 @@ namespace Content.Server.AI.Operators.Inventory
foreach (var slot in handsComponent.ActivePriorityEnumerable()) foreach (var slot in handsComponent.ActivePriorityEnumerable())
{ {
if (handsComponent.GetHand(slot) != itemComponent) continue; if (handsComponent.GetItem(slot) != itemComponent) continue;
handsComponent.ActiveIndex = slot; handsComponent.ActiveHand = slot;
handsComponent.ActivateItem(); handsComponent.ActivateItem();
return Outcome.Success; return Outcome.Success;
} }

View File

@@ -1,6 +1,7 @@
using Content.Server.AI.WorldState; using Content.Server.AI.WorldState;
using Content.Server.AI.WorldState.States; using Content.Server.AI.WorldState.States;
using Content.Server.GameObjects; using Content.Server.GameObjects;
using Content.Server.GameObjects.Components.GUI;
namespace Content.Server.AI.Utility.Considerations.Hands namespace Content.Server.AI.Utility.Considerations.Hands
{ {
@@ -21,7 +22,7 @@ namespace Content.Server.AI.Utility.Considerations.Hands
foreach (var hand in handsComponent.ActivePriorityEnumerable()) foreach (var hand in handsComponent.ActivePriorityEnumerable())
{ {
handCount++; handCount++;
if (handsComponent.GetHand(hand) == null) if (handsComponent.GetItem(hand) == null)
{ {
freeCount += 1; freeCount += 1;
} }

View File

@@ -1,7 +1,7 @@
using Content.Server.AI.WorldState; using Content.Server.AI.WorldState;
using Content.Server.AI.WorldState.States; using Content.Server.AI.WorldState.States;
using Content.Server.GameObjects;
using Content.Server.GameObjects.Components; using Content.Server.GameObjects.Components;
using Content.Server.GameObjects.Components.GUI;
namespace Content.Server.AI.Utility.Considerations.Hands namespace Content.Server.AI.Utility.Considerations.Hands
{ {

View File

@@ -1,4 +1,5 @@
using Content.Server.GameObjects; using Content.Server.GameObjects;
using Content.Server.GameObjects.Components.GUI;
using JetBrains.Annotations; using JetBrains.Annotations;
namespace Content.Server.AI.WorldState.States.Hands namespace Content.Server.AI.WorldState.States.Hands
@@ -16,7 +17,7 @@ namespace Content.Server.AI.WorldState.States.Hands
foreach (var hand in handsComponent.ActivePriorityEnumerable()) foreach (var hand in handsComponent.ActivePriorityEnumerable())
{ {
if (handsComponent.GetHand(hand) == null) if (handsComponent.GetItem(hand) == null)
{ {
return true; return true;
} }

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Content.Server.GameObjects; using Content.Server.GameObjects;
using Content.Server.GameObjects.Components.GUI;
using JetBrains.Annotations; using JetBrains.Annotations;
namespace Content.Server.AI.WorldState.States.Hands namespace Content.Server.AI.WorldState.States.Hands
@@ -20,7 +21,7 @@ namespace Content.Server.AI.WorldState.States.Hands
foreach (var hand in handsComponent.ActivePriorityEnumerable()) foreach (var hand in handsComponent.ActivePriorityEnumerable())
{ {
if (handsComponent.GetHand(hand) == null) if (handsComponent.GetItem(hand) == null)
{ {
result.Add(hand); result.Add(hand);
} }

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Content.Server.GameObjects; using Content.Server.GameObjects;
using Content.Server.GameObjects.Components.GUI;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
@@ -19,7 +20,7 @@ namespace Content.Server.AI.WorldState.States.Hands
foreach (var hand in handsComponent.ActivePriorityEnumerable()) foreach (var hand in handsComponent.ActivePriorityEnumerable())
{ {
var item = handsComponent.GetHand(hand); var item = handsComponent.GetItem(hand);
if (item != null) if (item != null)
{ {

View File

@@ -1,4 +1,5 @@
using Content.Server.GameObjects; using Content.Server.GameObjects;
using Content.Server.GameObjects.Components.GUI;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Content.Server.GameObjects; using Content.Server.GameObjects;
using Content.Server.GameObjects.Components.GUI;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;

View File

@@ -4,7 +4,6 @@ using Content.Server.Interfaces.Chat;
using Content.Server.Interfaces.GameObjects; using Content.Server.Interfaces.GameObjects;
using Content.Server.Players; using Content.Server.Players;
using Content.Shared.GameObjects; using Content.Shared.GameObjects;
using Robust.Server.Console;
using Robust.Server.Interfaces.Console; using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player; using Robust.Server.Interfaces.Player;
using Robust.Shared.Enums; using Robust.Shared.Enums;
@@ -13,6 +12,7 @@ using Robust.Shared.IoC;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using System.Linq; using System.Linq;
using Content.Server.GameObjects.Components; using Content.Server.GameObjects.Components;
using Content.Server.GameObjects.Components.GUI;
namespace Content.Server.Chat namespace Content.Server.Chat
{ {

View File

@@ -5,6 +5,7 @@ using System.Linq;
using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.Interfaces; using Content.Server.Interfaces;
using Content.Server.Interfaces.GameObjects; using Content.Server.Interfaces.GameObjects;
using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Shared.GameObjects.Components.Inventory; using Content.Shared.GameObjects.Components.Inventory;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;

View File

@@ -1,7 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Server.Interfaces; using Content.Server.Interfaces;
using Content.Server.Interfaces.GameObjects; using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Shared.Access; using Content.Shared.Access;
using Content.Shared.GameObjects.Components.Access; using Content.Shared.GameObjects.Components.Access;
using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Interfaces.GameObjects.Components;
@@ -132,7 +132,7 @@ namespace Content.Server.GameObjects.Components.Access
{ {
return; return;
} }
if(!hands.Drop(hands.ActiveIndex, container)) if(!hands.Drop(hands.ActiveHand, container))
{ {
_notifyManager.PopupMessage(Owner.Transform.GridPosition, user, _localizationManager.GetString("You can't let go of the ID card!")); _notifyManager.PopupMessage(Owner.Transform.GridPosition, user, _localizationManager.GetString("You can't let go of the ID card!"));
return; return;

View File

@@ -1,5 +1,6 @@
#nullable enable #nullable enable
using System; using System;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Strap; using Content.Server.GameObjects.Components.Strap;
using Content.Server.GameObjects.EntitySystems; using Content.Server.GameObjects.EntitySystems;

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.Interfaces.GameObjects.Components.Interaction; using Content.Server.Interfaces.GameObjects.Components.Interaction;
using Content.Server.Interfaces; using Content.Server.Interfaces;
using Content.Server.Interfaces.GameObjects; using Content.Server.Interfaces.GameObjects;
@@ -19,6 +20,7 @@ using Robust.Shared.ViewVariables;
using Robust.Server.GameObjects.EntitySystems; using Robust.Server.GameObjects.EntitySystems;
using Robust.Shared.GameObjects.Systems; using Robust.Shared.GameObjects.Systems;
using Content.Server.GameObjects.Components.Power.ApcNetComponents; using Content.Server.GameObjects.Components.Power.ApcNetComponents;
using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.Random; using Robust.Shared.Interfaces.Random;
using Robust.Shared.Maths; using Robust.Shared.Maths;

View File

@@ -1,8 +1,8 @@
using System; using System;
using System.Linq; using System.Linq;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.Interfaces.GameObjects.Components.Interaction; using Content.Server.Interfaces.GameObjects.Components.Interaction;
using Content.Server.Interfaces; using Content.Server.Interfaces;
using Content.Server.Interfaces.GameObjects;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Content.Shared.GameObjects.Components.Chemistry; using Content.Shared.GameObjects.Components.Chemistry;
using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems;
@@ -21,6 +21,7 @@ using Robust.Server.GameObjects.EntitySystems;
using Robust.Shared.GameObjects.Systems; using Robust.Shared.GameObjects.Systems;
using Content.Server.GameObjects.Components.Power.ApcNetComponents; using Content.Server.GameObjects.Components.Power.ApcNetComponents;
using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Interfaces.GameObjects.Components;
using Content.Server.Interfaces.GameObjects.Components.Items;
namespace Content.Server.GameObjects.Components.Chemistry namespace Content.Server.GameObjects.Components.Chemistry
{ {

View File

@@ -17,6 +17,7 @@ using Robust.Shared.Utility;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Server.GameObjects.Components.GUI;
using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems;
using Robust.Shared.GameObjects.Systems; using Robust.Shared.GameObjects.Systems;
using Content.Server.GameObjects.EntitySystems.Click; using Content.Server.GameObjects.EntitySystems.Click;

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using Content.Server.GameObjects.Components.Access; using Content.Server.GameObjects.Components.Access;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Server.Interfaces.GameObjects; using Content.Server.Interfaces.GameObjects;
using Content.Shared.GameObjects.Components.Doors; using Content.Shared.GameObjects.Components.Doors;

View File

@@ -1,10 +1,13 @@
using System; #nullable enable
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using Content.Server.GameObjects.Components; using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Shared.GameObjects.Components.Items;
using Content.Server.GameObjects.EntitySystems.Click; using Content.Server.GameObjects.EntitySystems.Click;
using Content.Server.Interfaces.GameObjects; using Content.Server.Interfaces.GameObjects.Components.Interaction;
using Content.Shared.GameObjects; using Content.Shared.BodySystem;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.GameObjects.Components.Container; using Robust.Server.GameObjects.Components.Container;
using Robust.Server.GameObjects.EntitySystemMessages; using Robust.Server.GameObjects.EntitySystemMessages;
@@ -18,72 +21,67 @@ using Robust.Shared.Log;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Players; using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects namespace Content.Server.GameObjects.Components.GUI
{ {
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(IHandsComponent))] [ComponentReference(typeof(IHandsComponent))]
public class HandsComponent : SharedHandsComponent, IHandsComponent public class HandsComponent : SharedHandsComponent, IHandsComponent, IBodyPartAdded, IBodyPartRemoved
{ {
#pragma warning disable 649 #pragma warning disable 649
[Dependency] private readonly IEntitySystemManager _entitySystemManager; [Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
#pragma warning restore 649 #pragma warning restore 649
private string _activeIndex; private string? _activeHand;
private uint _nextHand;
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
public string ActiveIndex public string? ActiveHand
{ {
get => _activeIndex; get => _activeHand;
set set
{ {
if (!_hands.ContainsKey(value)) if (value != null && GetHand(value) == null)
{ {
throw new ArgumentException($"No hand '{value}'"); throw new ArgumentException($"No hand '{value}'");
} }
_activeIndex = value; _activeHand = value;
Dirty(); Dirty();
} }
} }
[ViewVariables] private readonly Dictionary<string, ContainerSlot> _hands = new Dictionary<string, ContainerSlot>(); [ViewVariables] private readonly List<Hand> _hands = new List<Hand>();
[ViewVariables] private List<string> _orderedHands = new List<string>();
// Mostly arbitrary. // Mostly arbitrary.
public const float PickupRange = 2; public const float PickupRange = 2;
[ViewVariables] public int Count => _orderedHands.Count; [ViewVariables] public int Count => _hands.Count;
public override void ExposeData(ObjectSerializer serializer) // TODO: This does not serialize what objects are held.
protected override void Startup()
{ {
base.ExposeData(serializer); base.Startup();
ActiveHand = _hands.LastOrDefault()?.Name;
serializer.DataReadWriteFunction("hands",
new List<string>(0),
hands => hands.ForEach(AddHand),
() => _orderedHands);
serializer.DataField(ref _activeIndex, "defaultHand", _orderedHands.LastOrDefault());
} }
public IEnumerable<ItemComponent> GetAllHeldItems() public IEnumerable<ItemComponent> GetAllHeldItems()
{ {
foreach (var slot in _hands.Values) foreach (var hand in _hands)
{ {
if (slot.ContainedEntity != null) if (hand.Entity != null)
{ {
yield return slot.ContainedEntity.GetComponent<ItemComponent>(); yield return hand.Entity.GetComponent<ItemComponent>();
} }
} }
} }
public bool IsHolding(IEntity entity) public bool IsHolding(IEntity entity)
{ {
foreach (var slot in _hands.Values) foreach (var hand in _hands)
{ {
if (slot.ContainedEntity == entity) if (hand.Entity == entity)
{ {
return true; return true;
} }
@@ -91,28 +89,38 @@ namespace Content.Server.GameObjects
return false; return false;
} }
public ItemComponent GetHand(string index) private Hand? GetHand(string name)
{ {
var slot = _hands[index]; return _hands.FirstOrDefault(hand => hand.Name == name);
return slot.ContainedEntity?.GetComponent<ItemComponent>();
} }
public ItemComponent GetActiveHand => GetHand(ActiveIndex); public ItemComponent? GetItem(string handName)
{
return GetHand(handName)?.Entity?.GetComponent<ItemComponent>();
}
public ItemComponent? GetActiveHand => ActiveHand == null
? null
: GetItem(ActiveHand);
/// <summary> /// <summary>
/// Enumerates over the hand keys, returning the active hand first. /// Enumerates over the hand keys, returning the active hand first.
/// </summary> /// </summary>
public IEnumerable<string> ActivePriorityEnumerable() public IEnumerable<string> ActivePriorityEnumerable()
{ {
yield return ActiveIndex; if (ActiveHand != null)
foreach (var hand in _hands.Keys)
{ {
if (hand == ActiveIndex) yield return ActiveHand;
}
foreach (var hand in _hands)
{
if (hand.Name == ActiveHand)
{ {
continue; continue;
} }
yield return hand; yield return hand.Name;
} }
} }
@@ -120,7 +128,7 @@ namespace Content.Server.GameObjects
{ {
foreach (var hand in ActivePriorityEnumerable()) foreach (var hand in ActivePriorityEnumerable())
{ {
if (PutInHand(item, hand, fallback: false)) if (PutInHand(item, hand, false))
{ {
return true; return true;
} }
@@ -131,14 +139,14 @@ namespace Content.Server.GameObjects
public bool PutInHand(ItemComponent item, string index, bool fallback = true) public bool PutInHand(ItemComponent item, string index, bool fallback = true)
{ {
if (!CanPutInHand(item, index)) var hand = GetHand(index);
if (!CanPutInHand(item, index) || hand == null)
{ {
return fallback && PutInHand(item); return fallback && PutInHand(item);
} }
var slot = _hands[index];
Dirty(); Dirty();
var success = slot.Insert(item.Owner); var success = hand.Container.Insert(item.Owner);
if (success) if (success)
{ {
item.Owner.Transform.LocalPosition = Vector2.Zero; item.Owner.Transform.LocalPosition = Vector2.Zero;
@@ -152,14 +160,16 @@ namespace Content.Server.GameObjects
public void PutInHandOrDrop(ItemComponent item) public void PutInHandOrDrop(ItemComponent item)
{ {
if (!PutInHand(item)) if (!PutInHand(item))
{
item.Owner.Transform.GridPosition = Owner.Transform.GridPosition; item.Owner.Transform.GridPosition = Owner.Transform.GridPosition;
}
} }
public bool CanPutInHand(ItemComponent item) public bool CanPutInHand(ItemComponent item)
{ {
foreach (var hand in ActivePriorityEnumerable()) foreach (var handName in ActivePriorityEnumerable())
{ {
if (CanPutInHand(item, hand)) if (CanPutInHand(item, handName))
{ {
return true; return true;
} }
@@ -170,43 +180,42 @@ namespace Content.Server.GameObjects
public bool CanPutInHand(ItemComponent item, string index) public bool CanPutInHand(ItemComponent item, string index)
{ {
var slot = _hands[index]; return GetHand(index)?.Container.CanInsert(item.Owner) == true;
return slot.CanInsert(item.Owner);
} }
public string FindHand(IEntity entity) public bool TryHand(IEntity entity, [MaybeNullWhen(false)] out string handName)
{ {
foreach (var (index, slot) in _hands) handName = null;
foreach (var hand in _hands)
{ {
if (slot.ContainedEntity == entity) if (hand.Entity == entity)
{ {
return index; handName = hand.Name;
return true;
} }
} }
return null; return false;
} }
public bool Drop(string slot, GridCoordinates coords, bool doMobChecks = true) public bool Drop(string slot, GridCoordinates coords, bool doMobChecks = true)
{ {
if (!CanDrop(slot)) var hand = GetHand(slot);
if (!CanDrop(slot) || hand?.Entity == null)
{ {
return false; return false;
} }
var inventorySlot = _hands[slot]; var item = hand.Entity.GetComponent<ItemComponent>();
var item = inventorySlot.ContainedEntity.GetComponent<ItemComponent>();
if (!inventorySlot.Remove(inventorySlot.ContainedEntity)) if (!hand.Container.Remove(hand.Entity))
{ {
return false; return false;
} }
if (doMobChecks && !_entitySystemManager.GetEntitySystem<InteractionSystem>().TryDroppedInteraction(Owner, item.Owner)) if (doMobChecks &&
return false; !_entitySystemManager.GetEntitySystem<InteractionSystem>().TryDroppedInteraction(Owner, item.Owner))
if (ContainerHelpers.TryGetContainer(Owner, out var container) &&
!container.Insert(item.Owner))
{ {
return false; return false;
} }
@@ -214,6 +223,11 @@ namespace Content.Server.GameObjects
item.RemovedFromSlot(); item.RemovedFromSlot();
item.Owner.Transform.GridPosition = coords; item.Owner.Transform.GridPosition = coords;
if (ContainerHelpers.TryGetContainer(Owner, out var container))
{
container.Insert(item.Owner);
}
Dirty(); Dirty();
return true; return true;
} }
@@ -225,8 +239,7 @@ namespace Content.Server.GameObjects
throw new ArgumentNullException(nameof(entity)); throw new ArgumentNullException(nameof(entity));
} }
var slot = FindHand(entity); if (!TryHand(entity, out var slot))
if (slot == null)
{ {
throw new ArgumentException("Entity must be held in one of our hands.", nameof(entity)); throw new ArgumentException("Entity must be held in one of our hands.", nameof(entity));
} }
@@ -236,24 +249,21 @@ namespace Content.Server.GameObjects
public bool Drop(string slot, bool doMobChecks = true) public bool Drop(string slot, bool doMobChecks = true)
{ {
if (!CanDrop(slot)) var hand = GetHand(slot);
if (!CanDrop(slot) || hand?.Entity == null)
{ {
return false; return false;
} }
var inventorySlot = _hands[slot]; var item = hand.Entity.GetComponent<ItemComponent>();
var item = inventorySlot.ContainedEntity.GetComponent<ItemComponent>();
if (doMobChecks && !_entitySystemManager.GetEntitySystem<InteractionSystem>().TryDroppedInteraction(Owner, item.Owner)) if (doMobChecks &&
return false; !_entitySystemManager.GetEntitySystem<InteractionSystem>().TryDroppedInteraction(Owner, item.Owner))
if (!inventorySlot.Remove(inventorySlot.ContainedEntity))
{ {
return false; return false;
} }
if (ContainerHelpers.TryGetContainer(Owner, out var container) && if (!hand.Container.Remove(hand.Entity))
!container.Insert(item.Owner))
{ {
return false; return false;
} }
@@ -266,6 +276,11 @@ namespace Content.Server.GameObjects
spriteComponent.RenderOrder = item.Owner.EntityManager.CurrentTick.Value; spriteComponent.RenderOrder = item.Owner.EntityManager.CurrentTick.Value;
} }
if (ContainerHelpers.TryGetContainer(Owner, out var container))
{
container.Insert(item.Owner);
}
Dirty(); Dirty();
return true; return true;
} }
@@ -277,8 +292,7 @@ namespace Content.Server.GameObjects
throw new ArgumentNullException(nameof(entity)); throw new ArgumentNullException(nameof(entity));
} }
var slot = FindHand(entity); if (!TryHand(entity, out var slot))
if (slot == null)
{ {
throw new ArgumentException("Entity must be held in one of our hands.", nameof(entity)); throw new ArgumentException("Entity must be held in one of our hands.", nameof(entity));
} }
@@ -298,31 +312,30 @@ namespace Content.Server.GameObjects
throw new ArgumentNullException(nameof(targetContainer)); throw new ArgumentNullException(nameof(targetContainer));
} }
if (!CanDrop(slot)) var hand = GetHand(slot);
if (!CanDrop(slot) || hand?.Entity == null)
{ {
return false; return false;
} }
var item = hand.Entity.GetComponent<ItemComponent>();
var inventorySlot = _hands[slot];
var item = inventorySlot.ContainedEntity.GetComponent<ItemComponent>();
if (doMobChecks && !_entitySystemManager.GetEntitySystem<InteractionSystem>().TryDroppedInteraction(Owner, item.Owner)) if (doMobChecks && !_entitySystemManager.GetEntitySystem<InteractionSystem>().TryDroppedInteraction(Owner, item.Owner))
{ {
return false; return false;
} }
if (!inventorySlot.CanRemove(inventorySlot.ContainedEntity)) if (!hand.Container.CanRemove(hand.Entity))
{ {
return false; return false;
} }
if (!targetContainer.CanInsert(inventorySlot.ContainedEntity)) if (!targetContainer.CanInsert(hand.Entity))
{ {
return false; return false;
} }
if (!inventorySlot.Remove(inventorySlot.ContainedEntity)) if (!hand.Container.Remove(hand.Entity))
{ {
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
@@ -345,8 +358,7 @@ namespace Content.Server.GameObjects
throw new ArgumentNullException(nameof(entity)); throw new ArgumentNullException(nameof(entity));
} }
var slot = FindHand(entity); if (!TryHand(entity, out var slot))
if (slot == null)
{ {
throw new ArgumentException("Entity must be held in one of our hands.", nameof(entity)); throw new ArgumentException("Entity must be held in one of our hands.", nameof(entity));
} }
@@ -357,94 +369,103 @@ namespace Content.Server.GameObjects
/// <summary> /// <summary>
/// Checks whether an item can be dropped from the specified slot. /// Checks whether an item can be dropped from the specified slot.
/// </summary> /// </summary>
/// <param name="slot">The slot to check for.</param> /// <param name="name">The slot to check for.</param>
/// <returns> /// <returns>
/// True if there is an item in the slot and it can be dropped, false otherwise. /// True if there is an item in the slot and it can be dropped, false otherwise.
/// </returns> /// </returns>
public bool CanDrop(string slot) public bool CanDrop(string name)
{ {
var inventorySlot = _hands[slot]; var hand = GetHand(name);
if (hand?.Entity == null)
if (ContainerHelpers.TryGetContainer(Owner, out var container) &&
!container.CanInsert(inventorySlot.ContainedEntity))
{ {
return false; return false;
} }
return inventorySlot.CanRemove(inventorySlot.ContainedEntity); return hand.Container.CanRemove(hand.Entity);
} }
public void AddHand(string index) public void AddHand(string name)
{ {
if (HasHand(index)) if (HasHand(name))
{ {
throw new InvalidOperationException($"Hand '{index}' already exists."); throw new InvalidOperationException($"Hand '{name}' already exists.");
} }
var slot = ContainerManagerComponent.Create<ContainerSlot>(Name + "_" + index, Owner); var container = ContainerManagerComponent.Create<ContainerSlot>($"hand {_nextHand++}", Owner);
_hands[index] = slot; var hand = new Hand(name, container);
if (!_orderedHands.Contains(index))
{ _hands.Add(hand);
_orderedHands.Add(index);
} ActiveHand ??= name;
ActiveIndex ??= index;
Dirty(); Dirty();
} }
public void RemoveHand(string index) public void RemoveHand(string name)
{ {
if (!HasHand(index)) var hand = GetHand(name);
if (hand == null)
{ {
throw new InvalidOperationException($"Hand '{index}' does not exist."); throw new InvalidOperationException($"Hand '{name}' does not exist.");
} }
_hands[index].Shutdown(); //TODO verify this Drop(hand.Name, false);
_hands.Remove(index); hand!.Dispose();
_orderedHands.Remove(index); _hands.Remove(hand);
if (index == ActiveIndex) if (name == ActiveHand)
{ {
_activeIndex = _orderedHands.Count == 0 ? null : _orderedHands[0]; _activeHand = _hands.FirstOrDefault()?.Name;
} }
Dirty(); Dirty();
} }
public bool HasHand(string index) public bool HasHand(string name)
{ {
return _hands.ContainsKey(index); return _hands.Any(hand => hand.Name == name);
} }
/// <summary>
/// Get the name of the slot passed to the inventory component.
/// </summary>
private string HandSlotName(string index) => $"_hand_{index}";
public override ComponentState GetComponentState() public override ComponentState GetComponentState()
{ {
var dict = new Dictionary<string, EntityUid>(_hands.Count); var hands = new SharedHand[_hands.Count];
foreach (var hand in _hands)
for (var i = 0; i < _hands.Count; i++)
{ {
if (hand.Value.ContainedEntity != null) var location = i == 0
{ ? HandLocation.Right
dict[hand.Key] = hand.Value.ContainedEntity.Uid; : i == _hands.Count - 1
} ? HandLocation.Left
: HandLocation.Middle;
var hand = _hands[i].ToShared(i, location);
hands[i] = hand;
} }
return new HandsComponentState(dict, ActiveIndex); return new HandsComponentState(hands, ActiveHand);
} }
public void SwapHands() public void SwapHands()
{ {
var index = _orderedHands.FindIndex(x => x == ActiveIndex); if (ActiveHand == null)
{
return;
}
var hand = GetHand(ActiveHand);
if (hand == null)
{
throw new InvalidOperationException($"No hand found with name {ActiveHand}");
}
var index = _hands.IndexOf(hand);
index++; index++;
if (index >= _orderedHands.Count) if (index == _hands.Count)
{ {
index = 0; index = 0;
} }
ActiveIndex = _orderedHands[index]; ActiveHand = _hands[index].Name;
} }
public void ActivateItem() public void ActivateItem()
@@ -469,7 +490,7 @@ namespace Content.Server.GameObjects
return false; return false;
} }
public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession session = null) public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession? session = null)
{ {
base.HandleNetworkMessage(message, channel, session); base.HandleNetworkMessage(message, channel, session);
@@ -485,15 +506,19 @@ namespace Content.Server.GameObjects
var playerEntity = session.AttachedEntity; var playerEntity = session.AttachedEntity;
if (playerEntity == Owner && HasHand(msg.Index)) if (playerEntity == Owner && HasHand(msg.Index))
ActiveIndex = msg.Index; {
ActiveHand = msg.Index;
}
break; break;
} }
case ClientAttackByInHandMsg msg: case ClientAttackByInHandMsg msg:
{ {
if (!_hands.TryGetValue(msg.Index, out var slot)) var hand = GetHand(msg.Index);
if (hand == null)
{ {
Logger.WarningS("go.comp.hands", "Got a ClientAttackByInHandMsg with invalid hand index '{0}'", Logger.WarningS("go.comp.hands", "Got a ClientAttackByInHandMsg with invalid hand name '{0}'",
msg.Index); msg.Index);
return; return;
} }
@@ -501,19 +526,22 @@ namespace Content.Server.GameObjects
var playerEntity = session.AttachedEntity; var playerEntity = session.AttachedEntity;
var used = GetActiveHand?.Owner; var used = GetActiveHand?.Owner;
if (playerEntity == Owner && slot.ContainedEntity != null) if (playerEntity == Owner && hand.Entity != null)
{ {
var interactionSystem = _entitySystemManager.GetEntitySystem<InteractionSystem>(); var interactionSystem = _entitySystemManager.GetEntitySystem<InteractionSystem>();
if (used != null) if (used != null)
{ {
interactionSystem.Interaction(Owner, used, slot.ContainedEntity, interactionSystem.Interaction(Owner, used, hand.Entity,
GridCoordinates.InvalidGrid); GridCoordinates.InvalidGrid);
} }
else else
{ {
var entity = slot.ContainedEntity; var entity = hand.Entity;
if (!Drop(entity)) if (!Drop(entity))
{
break; break;
}
interactionSystem.Interaction(Owner, entity); interactionSystem.Interaction(Owner, entity);
} }
} }
@@ -521,7 +549,7 @@ namespace Content.Server.GameObjects
break; break;
} }
case UseInHandMsg msg: case UseInHandMsg _:
{ {
var playerEntity = session.AttachedEntity; var playerEntity = session.AttachedEntity;
var used = GetActiveHand?.Owner; var used = GetActiveHand?.Owner;
@@ -538,7 +566,7 @@ namespace Content.Server.GameObjects
case ActivateInHandMsg msg: case ActivateInHandMsg msg:
{ {
var playerEntity = session.AttachedEntity; var playerEntity = session.AttachedEntity;
var used = GetHand(msg.Index)?.Owner; var used = GetItem(msg.Index)?.Owner;
if (playerEntity == Owner && used != null) if (playerEntity == Owner && used != null)
{ {
@@ -552,9 +580,9 @@ namespace Content.Server.GameObjects
public void HandleSlotModifiedMaybe(ContainerModifiedMessage message) public void HandleSlotModifiedMaybe(ContainerModifiedMessage message)
{ {
foreach (var container in _hands.Values) foreach (var hand in _hands)
{ {
if (container != message.Container) if (hand.Container != message.Container)
{ {
continue; continue;
} }
@@ -571,5 +599,48 @@ namespace Content.Server.GameObjects
return; return;
} }
} }
void IBodyPartAdded.BodyPartAdded(BodyPartAddedEventArgs eventArgs)
{
if (eventArgs.Part.PartType != BodyPartType.Hand)
{
return;
}
AddHand(eventArgs.SlotName);
}
void IBodyPartRemoved.BodyPartRemoved(BodyPartRemovedEventArgs eventArgs)
{
if (eventArgs.Part.PartType != BodyPartType.Hand)
{
return;
}
RemoveHand(eventArgs.SlotName);
}
}
public class Hand : IDisposable
{
public Hand(string name, ContainerSlot container)
{
Name = name;
Container = container;
}
public string Name { get; }
public IEntity? Entity => Container.ContainedEntity;
public ContainerSlot Container { get; }
public void Dispose()
{
Container.Shutdown(); // TODO verify this
}
public SharedHand ToShared(int index, HandLocation location)
{
return new SharedHand(index, Name, Entity?.Uid, location);
}
} }
} }

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Server.GameObjects.Components; using Content.Server.GameObjects.Components;
using Content.Server.GameObjects.Components.GUI;
using Content.Shared.GameObjects.Components.Inventory; using Content.Shared.GameObjects.Components.Inventory;
using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.EntitySystems.Click; using Content.Server.GameObjects.EntitySystems.Click;
@@ -326,7 +327,7 @@ namespace Content.Server.GameObjects
var activeHand = hands.GetActiveHand; var activeHand = hands.GetActiveHand;
if (activeHand != null && activeHand.Owner.TryGetComponent(out ItemComponent clothing)) if (activeHand != null && activeHand.Owner.TryGetComponent(out ItemComponent clothing))
{ {
hands.Drop(hands.ActiveIndex); hands.Drop(hands.ActiveHand);
if (!Equip(msg.Inventoryslot, clothing, out var reason)) if (!Equip(msg.Inventoryslot, clothing, out var reason))
{ {
hands.PutInHand(clothing); hands.PutInHand(clothing);

View File

@@ -1,10 +1,12 @@
using Content.Server.GameObjects.Components.Power; using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Power;
using Content.Server.GameObjects.EntitySystems.Click; using Content.Server.GameObjects.EntitySystems.Click;
using Content.Server.Interfaces.GameObjects; using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Shared.GameObjects; using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components; using Content.Shared.GameObjects.Components;
using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Interfaces; using Content.Shared.Interfaces;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.GameObjects.Components.Container; using Robust.Server.GameObjects.Components.Container;
using Robust.Server.GameObjects.EntitySystems; using Robust.Server.GameObjects.EntitySystems;
@@ -16,7 +18,6 @@ using Robust.Shared.IoC;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
using Content.Shared.Interfaces.GameObjects.Components;
namespace Content.Server.GameObjects.Components.Interactable namespace Content.Server.GameObjects.Components.Interactable
{ {

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Content.Server.GameObjects.Components; using Content.Server.GameObjects.Components;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.Interfaces; using Content.Server.Interfaces;
using Content.Shared.GameObjects; using Content.Shared.GameObjects;

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Interactable; using Content.Server.GameObjects.Components.Interactable;
using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.Interfaces.GameObjects.Components.Interaction; using Content.Server.Interfaces.GameObjects.Components.Interaction;

View File

@@ -1,6 +1,8 @@
using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.Interfaces.GameObjects.Components.Interaction; using Content.Server.Interfaces.GameObjects.Components.Interaction;
using Content.Server.Interfaces.GameObjects; using Content.Server.Interfaces.GameObjects;
using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Server.Throw; using Content.Server.Throw;
using Content.Server.Utility; using Content.Server.Utility;
using Content.Shared.GameObjects; using Content.Shared.GameObjects;
@@ -100,7 +102,7 @@ namespace Content.Server.GameObjects.Components
if (!CanPickup(eventArgs.User)) return false; if (!CanPickup(eventArgs.User)) return false;
var hands = eventArgs.User.GetComponent<IHandsComponent>(); var hands = eventArgs.User.GetComponent<IHandsComponent>();
hands.PutInHand(this, hands.ActiveIndex, fallback: false); hands.PutInHand(this, hands.ActiveHand, false);
return true; return true;
} }

View File

@@ -2,8 +2,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.Interfaces.GameObjects; using Content.Server.Interfaces.GameObjects;
using Content.Server.Interfaces.GameObjects.Components.Interaction; using Content.Server.Interfaces.GameObjects.Components.Interaction;
using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Server.Utility; using Content.Server.Utility;
using Content.Shared.GameObjects.Components.Storage; using Content.Shared.GameObjects.Components.Storage;
using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems;

View File

@@ -1,10 +1,12 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Server.BodySystem;
using Content.Server.Interfaces.GameObjects.Components.Interaction; using Content.Server.Interfaces.GameObjects.Components.Interaction;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
using Content.Server.GameObjects.Components.Chemistry; using Content.Server.GameObjects.Components.Chemistry;
using Content.Server.GameObjects.Components.GUI;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
@@ -22,10 +24,10 @@ using Content.Server.Interfaces;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Content.Server.Interfaces.GameObjects; using Content.Server.Interfaces.GameObjects;
using Content.Server.Interfaces.Chat; using Content.Server.Interfaces.Chat;
using Content.Server.BodySystem;
using Content.Shared.BodySystem; using Content.Shared.BodySystem;
using Robust.Shared.GameObjects.Systems; using Robust.Shared.GameObjects.Systems;
using Content.Server.GameObjects.Components.Power.ApcNetComponents; using Content.Server.GameObjects.Components.Power.ApcNetComponents;
using Content.Shared.Interfaces;
using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Interfaces.GameObjects.Components;
namespace Content.Server.GameObjects.Components.Kitchen namespace Content.Server.GameObjects.Components.Kitchen
@@ -211,9 +213,15 @@ namespace Content.Server.GameObjects.Components.Kitchen
return false; return false;
} }
var itemEntity = eventArgs.User.GetComponent<HandsComponent>().GetActiveHand.Owner; var itemEntity = eventArgs.User.GetComponent<HandsComponent>().GetActiveHand?.Owner;
if(itemEntity.TryGetComponent<PourableComponent>(out var attackPourable)) if (itemEntity == null)
{
eventArgs.User.PopupMessage(eventArgs.User, Loc.GetString("You have no active hand!"));
return false;
}
if (itemEntity.TryGetComponent<PourableComponent>(out var attackPourable))
{ {
if (!itemEntity.TryGetComponent<SolutionComponent>(out var attackSolution) if (!itemEntity.TryGetComponent<SolutionComponent>(out var attackSolution)
|| !attackSolution.CanPourOut) || !attackSolution.CanPourOut)

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Content.Server.GameObjects.Components.Chemistry; using Content.Server.GameObjects.Components.Chemistry;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Utensil; using Content.Server.GameObjects.Components.Utensil;
using Content.Server.Utility; using Content.Server.Utility;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Server.GameObjects.Components.GUI;
using Content.Shared.GameObjects.Components.Nutrition; using Content.Shared.GameObjects.Components.Nutrition;
using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
@@ -54,8 +55,11 @@ namespace Content.Server.GameObjects.Components.Nutrition
bool IUse.UseEntity(UseEntityEventArgs eventArgs) bool IUse.UseEntity(UseEntityEventArgs eventArgs)
{ {
if (!eventArgs.User.TryGetComponent(out HandsComponent handsComponent))
{
return false;
}
var hands = eventArgs.User.TryGetComponent(out HandsComponent handsComponent);
var itemToSpawn = _entityManager.SpawnEntity(GetRandomPrototype(), Owner.Transform.GridPosition); var itemToSpawn = _entityManager.SpawnEntity(GetRandomPrototype(), Owner.Transform.GridPosition);
handsComponent.PutInHandOrDrop(itemToSpawn.GetComponent<ItemComponent>()); handsComponent.PutInHandOrDrop(itemToSpawn.GetComponent<ItemComponent>());
_count--; _count--;

View File

@@ -4,6 +4,7 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Server.GameObjects.Components.Access; using Content.Server.GameObjects.Components.Access;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.Interfaces; using Content.Server.Interfaces;
using Content.Server.Interfaces.PDA; using Content.Server.Interfaces.PDA;
using Content.Shared.GameObjects; using Content.Shared.GameObjects;

View File

@@ -1,4 +1,5 @@
using Content.Shared.GameObjects.Components; using Content.Server.GameObjects.Components.GUI;
using Content.Shared.GameObjects.Components;
using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
@@ -17,6 +18,7 @@ namespace Content.Server.GameObjects.Components
serializer.DataField(ref _isPlaceable, "IsPlaceable", true); serializer.DataField(ref _isPlaceable, "IsPlaceable", true);
} }
public bool InteractUsing(InteractUsingEventArgs eventArgs) public bool InteractUsing(InteractUsingEventArgs eventArgs)
{ {
if (!IsPlaceable) if (!IsPlaceable)

View File

@@ -1,5 +1,6 @@
using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.Components.GUI;
using Content.Server.Interfaces.GameObjects; using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Shared.Audio; using Content.Shared.Audio;
using Content.Shared.Interfaces; using Content.Shared.Interfaces;
using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Interfaces.GameObjects.Components;

View File

@@ -1,4 +1,5 @@
using System; using System;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Power.ApcNetComponents; using Content.Server.GameObjects.Components.Power.ApcNetComponents;
using Content.Server.GameObjects.Components.Weapon.Ranged.Barrels; using Content.Server.GameObjects.Components.Weapon.Ranged.Barrels;
using Content.Shared.GameObjects; using Content.Shared.GameObjects;

View File

@@ -1,4 +1,5 @@
using System; using System;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Power.ApcNetComponents; using Content.Server.GameObjects.Components.Power.ApcNetComponents;
using Content.Server.GameObjects.EntitySystems; using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces; using Content.Server.Interfaces;

View File

@@ -1,8 +1,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Power; using Content.Server.GameObjects.Components.Power;
using Content.Server.GameObjects.EntitySystems.Click; using Content.Server.GameObjects.EntitySystems.Click;
using Content.Server.Interfaces.GameObjects; using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Shared.Audio; using Content.Shared.Audio;
using Content.Shared.GameObjects; using Content.Shared.GameObjects;
using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems;

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Weapon.Ranged.Barrels; using Content.Server.GameObjects.Components.Weapon.Ranged.Barrels;
using Content.Shared.GameObjects; using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels; using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels;

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Weapon.Ranged.Barrels; using Content.Server.GameObjects.Components.Weapon.Ranged.Barrels;
using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels; using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels;
using Content.Shared.Interfaces; using Content.Shared.Interfaces;

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Weapon.Ranged.Barrels; using Content.Server.GameObjects.Components.Weapon.Ranged.Barrels;
using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels; using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels;
using Content.Shared.Interfaces; using Content.Shared.Interfaces;

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Power; using Content.Server.GameObjects.Components.Power;
using Content.Server.GameObjects.Components.Projectiles; using Content.Server.GameObjects.Components.Projectiles;
using Content.Shared.GameObjects; using Content.Shared.GameObjects;

View File

@@ -1,6 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition; using Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition;
using Content.Server.GameObjects.EntitySystems.Click; using Content.Server.GameObjects.EntitySystems.Click;
using Content.Shared.GameObjects; using Content.Shared.GameObjects;

View File

@@ -1,4 +1,5 @@
using System; using System;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Weapon.Ranged.Barrels; using Content.Server.GameObjects.Components.Weapon.Ranged.Barrels;
using Content.Shared.GameObjects.Components.Weapons.Ranged; using Content.Shared.GameObjects.Components.Weapons.Ranged;

View File

@@ -8,6 +8,7 @@ using Content.Server.GameObjects.EntitySystems.Click;
using Content.Server.Interfaces.GameObjects.Components.Interaction; using Content.Server.Interfaces.GameObjects.Components.Interaction;
using Content.Server.Interfaces; using Content.Server.Interfaces;
using Content.Server.Interfaces.GameObjects; using Content.Server.Interfaces.GameObjects;
using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Shared.GameObjects.Components; using Content.Shared.GameObjects.Components;
using Content.Shared.GameObjects.Components.Interactable; using Content.Shared.GameObjects.Components.Interactable;
using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems;

View File

@@ -2,7 +2,7 @@
using System.Linq; using System.Linq;
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Timing; using Content.Server.GameObjects.Components.Timing;
using Content.Server.Interfaces.GameObjects; using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Server.Utility; using Content.Server.Utility;
using Content.Shared.GameObjects.Components.Inventory; using Content.Shared.GameObjects.Components.Inventory;
using Content.Shared.GameObjects.EntitySystemMessages; using Content.Shared.GameObjects.EntitySystemMessages;

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using Content.Server.GameObjects.Components; using Content.Server.GameObjects.Components;
using Content.Server.GameObjects.Components.Construction; using Content.Server.GameObjects.Components.Construction;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Interactable; using Content.Server.GameObjects.Components.Interactable;
using Content.Server.GameObjects.Components.Stack; using Content.Server.GameObjects.Components.Stack;
using Content.Server.GameObjects.EntitySystems.Click; using Content.Server.GameObjects.EntitySystems.Click;

View File

@@ -1,5 +1,6 @@
using System.Linq; using System.Linq;
using Content.Server.GameObjects.Components.Stack; using Content.Server.GameObjects.Components.Stack;
using Content.Server.Interfaces;
using Content.Server.Throw; using Content.Server.Throw;
using Content.Shared.GameObjects.Components.Inventory; using Content.Shared.GameObjects.Components.Inventory;
using Content.Shared.Input; using Content.Shared.Input;
@@ -15,6 +16,8 @@ using Robust.Shared.Localization;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Players; using Robust.Shared.Players;
using System; using System;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems;
using Content.Server.GameObjects; using Content.Server.GameObjects;
using Content.Server.GameObjects.Components; using Content.Server.GameObjects.Components;
@@ -74,11 +77,10 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
var ent = session.AttachedEntity; var ent = session.AttachedEntity;
if (ent == null || !ent.IsValid()) if (ent == null || !ent.IsValid() || !ent.TryGetComponent(out T comp))
return false; {
if (!ent.TryGetComponent(out T comp))
return false; return false;
}
component = comp; component = comp;
return true; return true;
@@ -87,9 +89,11 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
private static void HandleSwapHands(ICommonSession session) private static void HandleSwapHands(ICommonSession session)
{ {
if (!TryGetAttachedComponent(session as IPlayerSession, out HandsComponent handsComp)) if (!TryGetAttachedComponent(session as IPlayerSession, out HandsComponent handsComp))
{
return; return;
}
var interactionSystem = EntitySystem.Get<InteractionSystem>(); var interactionSystem = Get<InteractionSystem>();
var oldItem = handsComp.GetActiveHand; var oldItem = handsComp.GetActiveHand;
@@ -97,11 +101,15 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
var newItem = handsComp.GetActiveHand; var newItem = handsComp.GetActiveHand;
if(oldItem != null) if (oldItem != null)
{
interactionSystem.HandDeselectedInteraction(handsComp.Owner, oldItem.Owner); interactionSystem.HandDeselectedInteraction(handsComp.Owner, oldItem.Owner);
}
if(newItem != null) if (newItem != null)
{
interactionSystem.HandSelectedInteraction(handsComp.Owner, newItem.Owner); interactionSystem.HandSelectedInteraction(handsComp.Owner, newItem.Owner);
}
} }
private bool HandleDrop(ICommonSession session, GridCoordinates coords, EntityUid uid) private bool HandleDrop(ICommonSession session, GridCoordinates coords, EntityUid uid)
@@ -119,11 +127,11 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
var entCoords = ent.Transform.GridPosition.Position; var entCoords = ent.Transform.GridPosition.Position;
var entToDesiredDropCoords = coords.Position - entCoords; var entToDesiredDropCoords = coords.Position - entCoords;
var targetLength = Math.Min(entToDesiredDropCoords.Length, InteractionSystem.InteractionRange - 0.001f); // InteractionRange is reduced due to InRange not dealing with floating point error var targetLength = Math.Min(entToDesiredDropCoords.Length, SharedInteractionSystem.InteractionRange - 0.001f); // InteractionRange is reduced due to InRange not dealing with floating point error
var newCoords = new GridCoordinates((entToDesiredDropCoords.Normalized * targetLength) + entCoords, coords.GridID); var newCoords = new GridCoordinates((entToDesiredDropCoords.Normalized * targetLength) + entCoords, coords.GridID);
var rayLength = EntitySystem.Get<SharedInteractionSystem>().UnobstructedRayLength(ent.Transform.MapPosition, newCoords.ToMap(_mapManager), ignoredEnt: ent); var rayLength = Get<SharedInteractionSystem>().UnobstructedRayLength(ent.Transform.MapPosition, newCoords.ToMap(_mapManager), ignoredEnt: ent);
handsComp.Drop(handsComp.ActiveIndex, new GridCoordinates(entCoords + (entToDesiredDropCoords.Normalized * rayLength), coords.GridID)); handsComp.Drop(handsComp.ActiveHand, new GridCoordinates(entCoords + (entToDesiredDropCoords.Normalized * rayLength), coords.GridID));
return true; return true;
} }
@@ -146,10 +154,10 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
if (!plyEnt.TryGetComponent(out HandsComponent handsComp)) if (!plyEnt.TryGetComponent(out HandsComponent handsComp))
return false; return false;
if (!handsComp.CanDrop(handsComp.ActiveIndex)) if (!handsComp.CanDrop(handsComp.ActiveHand))
return false; return false;
var throwEnt = handsComp.GetHand(handsComp.ActiveIndex).Owner; var throwEnt = handsComp.GetItem(handsComp.ActiveHand).Owner;
if (!handsComp.ThrowItem()) if (!handsComp.ThrowItem())
return false; return false;
@@ -157,7 +165,7 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
// throw the item, split off from a stack if it's meant to be thrown individually // throw the item, split off from a stack if it's meant to be thrown individually
if (!throwEnt.TryGetComponent(out StackComponent stackComp) || stackComp.Count < 2 || !stackComp.ThrowIndividually) if (!throwEnt.TryGetComponent(out StackComponent stackComp) || stackComp.Count < 2 || !stackComp.ThrowIndividually)
{ {
handsComp.Drop(handsComp.ActiveIndex); handsComp.Drop(handsComp.ActiveHand);
} }
else else
{ {
@@ -201,7 +209,7 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
return; return;
} }
var heldItem = handsComp.GetHand(handsComp.ActiveIndex)?.Owner; var heldItem = handsComp.GetItem(handsComp.ActiveHand)?.Owner;
if (heldItem != null) if (heldItem != null)
{ {

View File

@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using Content.Server.GameObjects; using Content.Server.GameObjects;
using Content.Server.GameObjects.Components; using Content.Server.GameObjects.Components;
using Content.Server.GameObjects.Components.Access; using Content.Server.GameObjects.Components.Access;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Markers; using Content.Server.GameObjects.Components.Markers;
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Observer; using Content.Server.GameObjects.Components.Observer;

View File

@@ -1,7 +1,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Content.Server.BodySystem;
using Content.Server.Interfaces.GameTicking; using Content.Server.Interfaces.GameTicking;
using Content.Server.Players; using Content.Server.Players;
using Content.Shared.BodySystem;
using Content.Shared.Jobs; using Content.Shared.Jobs;
using Robust.Server.Interfaces.Console; using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player; using Robust.Server.Interfaces.Player;
@@ -321,4 +324,56 @@ namespace Content.Server.GameTicking
shell.SendText(player, $"Created unloaded map from file {args[1]} with id {args[0]}. Use \"savebp 4 foo.yml\" to save it."); shell.SendText(player, $"Created unloaded map from file {args[1]} with id {args[0]}. Use \"savebp 4 foo.yml\" to save it.");
} }
} }
class AddHandCommand : IClientCommand
{
public string Command => "addhand";
public string Description => "Adds a hand to your entity.";
public string Help => $"Usage: {Command}";
public void Execute(IConsoleShell shell, IPlayerSession player, string[] args)
{
if (player == null)
{
shell.SendText(player, "Only a player can run this command.");
return;
}
if (player.AttachedEntity == null)
{
shell.SendText(player, "You have no entity.");
return;
}
var manager = player.AttachedEntity.GetComponent<BodyManagerComponent>();
var hand = manager.PartDictionary.First(x => x.Key == string.Join(" ", args));
manager.InstallBodyPart(hand.Value, hand.Key + new Random());
}
}
class RemoveHandCommand : IClientCommand
{
public string Command => "removehand";
public string Description => "Removes a hand from your entity.";
public string Help => $"Usage: {Command}";
public void Execute(IConsoleShell shell, IPlayerSession player, string[] args)
{
if (player == null)
{
shell.SendText(player, "Only a player can run this command.");
return;
}
if (player.AttachedEntity == null)
{
shell.SendText(player, "You have no entity.");
return;
}
var manager = player.AttachedEntity.GetComponent<BodyManagerComponent>();
var hand = manager.PartDictionary.First(x => x.Value.PartType == BodyPartType.Hand);
manager.DisconnectBodyPart(hand.Value, true);
}
}
} }

View File

@@ -8,6 +8,7 @@ using Content.Shared.BodySystem;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
using System.Linq; using System.Linq;
using Content.Server.Interfaces.GameObjects.Components.Interaction;
namespace Content.Server.BodySystem { namespace Content.Server.BodySystem {
@@ -178,8 +179,7 @@ namespace Content.Server.BodySystem {
///////// Server-specific stuff ///////// Server-specific stuff
///////// /////////
public override void ExposeData(ObjectSerializer serializer) public override void ExposeData(ObjectSerializer serializer) {
{
base.ExposeData(serializer); base.ExposeData(serializer);
serializer.DataReadWriteFunction( serializer.DataReadWriteFunction(
@@ -225,8 +225,16 @@ namespace Content.Server.BodySystem {
if (!_prototypeManager.TryIndex(partID, out BodyPartPrototype newPartData)) { //Get the BodyPartPrototype corresponding to the BodyPart ID we grabbed. if (!_prototypeManager.TryIndex(partID, out BodyPartPrototype newPartData)) { //Get the BodyPartPrototype corresponding to the BodyPart ID we grabbed.
throw new InvalidOperationException("BodyPart prototype with ID " + partID + " could not be found!"); throw new InvalidOperationException("BodyPart prototype with ID " + partID + " could not be found!");
} }
_partDictionary.Remove(slotName); //Try and remove an existing limb if that exists.
_partDictionary.Add(slotName, new BodyPart(newPartData)); //Add a new BodyPart with the BodyPartPrototype as a baseline to our BodyComponent. //Try and remove an existing limb if that exists.
if (_partDictionary.Remove(slotName, out var removedPart))
{
BodyPartRemoved(removedPart, slotName);
}
var addedPart = new BodyPart(newPartData);
_partDictionary.Add(slotName, addedPart); //Add a new BodyPart with the BodyPartPrototype as a baseline to our BodyComponent.
BodyPartAdded(addedPart, slotName);
} }
} }
@@ -264,6 +272,8 @@ namespace Content.Server.BodySystem {
if (TryGetBodyPart(slotName, out BodyPart result)) //And that nothing is in it if (TryGetBodyPart(slotName, out BodyPart result)) //And that nothing is in it
return false; return false;
_partDictionary.Add(slotName, part); _partDictionary.Add(slotName, part);
BodyPartAdded(part, slotName);
return true; return true;
} }
/// <summary> /// <summary>
@@ -316,7 +326,11 @@ namespace Content.Server.BodySystem {
return; return;
if (part != null) { if (part != null) {
string slotName = _partDictionary.FirstOrDefault(x => x.Value == part).Key; string slotName = _partDictionary.FirstOrDefault(x => x.Value == part).Key;
_partDictionary.Remove(slotName); if (_partDictionary.Remove(slotName, out var partRemoved))
{
BodyPartRemoved(partRemoved, slotName);
}
if (TryGetBodyPartConnections(slotName, out List<string> connections)) //Call disconnect on all limbs that were hanging off this limb. if (TryGetBodyPartConnections(slotName, out List<string> connections)) //Call disconnect on all limbs that were hanging off this limb.
{ {
foreach (string connectionName in connections) //This loop is an unoptimized travesty. TODO: optimize to be less shit foreach (string connectionName in connections) //This loop is an unoptimized travesty. TODO: optimize to be less shit
@@ -340,7 +354,11 @@ namespace Content.Server.BodySystem {
if (!TryGetBodyPart(name, out BodyPart part)) if (!TryGetBodyPart(name, out BodyPart part))
return; return;
if (part != null) { if (part != null) {
_partDictionary.Remove(name); if (_partDictionary.Remove(name, out var partRemoved))
{
BodyPartRemoved(partRemoved, name);
}
if (TryGetBodyPartConnections(name, out List<string> connections)) { if (TryGetBodyPartConnections(name, out List<string> connections)) {
foreach (string connectionName in connections) { foreach (string connectionName in connections) {
if (TryGetBodyPart(connectionName, out BodyPart result) && !ConnectedToCenterPart(result)) { if (TryGetBodyPart(connectionName, out BodyPart result) && !ConnectedToCenterPart(result)) {
@@ -355,5 +373,24 @@ namespace Content.Server.BodySystem {
} }
} }
private void BodyPartAdded(BodyPart part, string slotName)
{
var argsAdded = new BodyPartAddedEventArgs(part, slotName);
foreach (var component in Owner.GetAllComponents<IBodyPartAdded>().ToArray())
{
component.BodyPartAdded(argsAdded);
}
}
private void BodyPartRemoved(BodyPart part, string slotName)
{
var args = new BodyPartRemovedEventArgs(part, slotName);
foreach (var component in Owner.GetAllComponents<IBodyPartRemoved>())
{
component.BodyPartRemoved(args);
}
}
} }
} }

View File

@@ -0,0 +1,49 @@
using System;
using Content.Server.BodySystem;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
{
/// <summary>
/// This interface gives components behavior when a body part
/// is added to their owning entity.
/// </summary>
public interface IBodyPartAdded
{
void BodyPartAdded(BodyPartAddedEventArgs eventArgs);
}
public class BodyPartAddedEventArgs : EventArgs
{
public BodyPartAddedEventArgs(BodyPart part, string slotName)
{
Part = part;
SlotName = slotName;
}
public BodyPart Part { get; }
public string SlotName { get; }
}
/// <summary>
/// This interface gives components behavior when a body part
/// is removed from their owning entity.
/// </summary>
public interface IBodyPartRemoved
{
void BodyPartRemoved(BodyPartRemovedEventArgs eventArgs);
}
public class BodyPartRemovedEventArgs : EventArgs
{
public BodyPartRemovedEventArgs(BodyPart part, string slotName)
{
Part = part;
SlotName = slotName;
}
public BodyPart Part { get; }
public string SlotName { get; }
}
}

View File

@@ -7,14 +7,14 @@ using Robust.Server.GameObjects.EntitySystemMessages;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Map; using Robust.Shared.Map;
namespace Content.Server.Interfaces.GameObjects namespace Content.Server.Interfaces.GameObjects.Components.Items
{ {
public interface IHandsComponent : IComponent public interface IHandsComponent : IComponent
{ {
/// <summary> /// <summary>
/// The hand index of the currently active hand. /// The hand name of the currently active hand.
/// </summary> /// </summary>
string ActiveIndex { get; set; } string ActiveHand { get; set; }
/// <summary> /// <summary>
/// Enumerates over every held item. /// Enumerates over every held item.
@@ -24,9 +24,9 @@ namespace Content.Server.Interfaces.GameObjects
/// <summary> /// <summary>
/// Gets the item held by a hand. /// Gets the item held by a hand.
/// </summary> /// </summary>
/// <param name="index">The index of the hand to get.</param> /// <param name="handName">The name of the hand to get.</param>
/// <returns>The item in the held, null if no item is held</returns> /// <returns>The item in the held, null if no item is held</returns>
ItemComponent GetHand(string index); ItemComponent GetItem(string handName);
/// <summary> /// <summary>
/// Gets item held by the current active hand /// Gets item held by the current active hand
@@ -44,7 +44,7 @@ namespace Content.Server.Interfaces.GameObjects
/// Puts an item into a specific hand. /// Puts an item into a specific hand.
/// </summary> /// </summary>
/// <param name="item">The item to put in the hand.</param> /// <param name="item">The item to put in the hand.</param>
/// <param name="index">The index of the hand to put the item into.</param> /// <param name="index">The name of the hand to put the item into.</param>
/// <param name="fallback"> /// <param name="fallback">
/// If true and the provided hand is full, the method will fall back to <see cref="PutInHand(ItemComponent)" /> /// If true and the provided hand is full, the method will fall back to <see cref="PutInHand(ItemComponent)" />
/// </param> /// </param>
@@ -62,20 +62,22 @@ namespace Content.Server.Interfaces.GameObjects
/// Checks to see if an item can be put in the specified hand. /// Checks to see if an item can be put in the specified hand.
/// </summary> /// </summary>
/// <param name="item">The item to check for.</param> /// <param name="item">The item to check for.</param>
/// <param name="index">The index for the hand to check for.</param> /// <param name="index">The name for the hand to check for.</param>
/// <returns>True if the item can be inserted, false otherwise.</returns> /// <returns>True if the item can be inserted, false otherwise.</returns>
bool CanPutInHand(ItemComponent item, string index); bool CanPutInHand(ItemComponent item, string index);
/// <summary> /// <summary>
/// Finds the hand slot holding the specified entity, if any. /// Finds the hand slot holding the specified entity, if any.
/// </summary> /// </summary>
/// <param name="entity"> /// <param name="entity">The entity to look for in our hands.</param>
/// The entity to look for in our hands. /// <param name="handName">
/// The name of the hand slot if the entity is indeed held,
/// <see langword="null" /> otherwise.
/// </param> /// </param>
/// <returns> /// <returns>
/// The index of the hand slot if the entity is indeed held, <see langword="null" /> otherwise. /// true if the entity is held, false otherwise
/// </returns> /// </returns>
string FindHand(IEntity entity); bool TryHand(IEntity entity, out string handName);
/// <summary> /// <summary>
/// Drops the item contained in the slot to the same position as our entity. /// Drops the item contained in the slot to the same position as our entity.
@@ -135,7 +137,7 @@ namespace Content.Server.Interfaces.GameObjects
/// </summary> /// </summary>
/// <param name="slot">The slot of which to drop the entity.</param> /// <param name="slot">The slot of which to drop the entity.</param>
/// <param name="targetContainer">The container to drop into.</param> /// <param name="targetContainer">The container to drop into.</param>
/// <param name="doMobChecks">Whether to check the <see cref="ActionBlockerSystem.CanDrop()"/> for the mob or not.</param> /// <param name="doMobChecks">Whether to check the <see cref="ActionBlockerSystem.CanDrop(IEntity)"/> for the mob or not.</param>
/// <returns>True on success, false if something was blocked (insertion or removal).</returns> /// <returns>True on success, false if something was blocked (insertion or removal).</returns>
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// Thrown if dry-run checks reported OK to remove and insert, /// Thrown if dry-run checks reported OK to remove and insert,
@@ -167,20 +169,20 @@ namespace Content.Server.Interfaces.GameObjects
/// <summary> /// <summary>
/// Checks whether the item in the specified hand can be dropped. /// Checks whether the item in the specified hand can be dropped.
/// </summary> /// </summary>
/// <param name="index">The hand to check for.</param> /// <param name="name">The hand to check for.</param>
/// <returns> /// <returns>
/// True if the item can be dropped, false if the hand is empty or the item in the hand cannot be dropped. /// True if the item can be dropped, false if the hand is empty or the item in the hand cannot be dropped.
/// </returns> /// </returns>
bool CanDrop(string index); bool CanDrop(string name);
/// <summary> /// <summary>
/// Adds a new hand to this hands component. /// Adds a new hand to this hands component.
/// </summary> /// </summary>
/// <param name="index">The name of the hand to add.</param> /// <param name="name">The name of the hand to add.</param>
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// Thrown if a hand with specified name already exists. /// Thrown if a hand with specified name already exists.
/// </exception> /// </exception>
void AddHand(string index); void AddHand(string name);
/// <summary> /// <summary>
/// Removes a hand from this hands component. /// Removes a hand from this hands component.
@@ -188,15 +190,15 @@ namespace Content.Server.Interfaces.GameObjects
/// <remarks> /// <remarks>
/// If the hand contains an item, the item is dropped. /// If the hand contains an item, the item is dropped.
/// </remarks> /// </remarks>
/// <param name="index">The name of the hand to remove.</param> /// <param name="name">The name of the hand to remove.</param>
void RemoveHand(string index); void RemoveHand(string name);
/// <summary> /// <summary>
/// Checks whether a hand with the specified name exists. /// Checks whether a hand with the specified name exists.
/// </summary> /// </summary>
/// <param name="index">The hand name to check.</param> /// <param name="name">The hand name to check.</param>
/// <returns>True if the hand exists, false otherwise.</returns> /// <returns>True if the hand exists, false otherwise.</returns>
bool HasHand(string index); bool HasHand(string name);
void HandleSlotModifiedMaybe(ContainerModifiedMessage message); void HandleSlotModifiedMaybe(ContainerModifiedMessage message);
} }

View File

@@ -1,4 +1,5 @@
using Content.Server.Interfaces.GameObjects; using Content.Server.Interfaces.GameObjects;
using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Shared.Audio; using Content.Shared.Audio;
using Content.Shared.GameObjects.Components.Mobs; using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems;

View File

@@ -2,6 +2,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Server.GameObjects; using Content.Server.GameObjects;
using Content.Server.GameObjects.Components; using Content.Server.GameObjects.Components;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Server.Interfaces.PDA; using Content.Server.Interfaces.PDA;
using Content.Shared.GameObjects.Components.PDA; using Content.Shared.GameObjects.Components.PDA;

View File

@@ -1,5 +1,6 @@
using Content.Server.GameObjects; using Content.Server.GameObjects;
using Content.Server.GameObjects.Components; using Content.Server.GameObjects.Components;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.Interfaces.GameTicking; using Content.Server.Interfaces.GameTicking;
using Content.Shared.Sandbox; using Content.Shared.Sandbox;

View File

@@ -1,9 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using JetBrains.Annotations;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects namespace Content.Shared.GameObjects.Components.Items
{ {
public abstract class SharedHandsComponent : Component public abstract class SharedHandsComponent : Component
{ {
@@ -11,14 +13,31 @@ namespace Content.Shared.GameObjects
public sealed override uint? NetID => ContentNetIDs.HANDS; public sealed override uint? NetID => ContentNetIDs.HANDS;
} }
[Serializable, NetSerializable]
public sealed class SharedHand
{
public readonly int Index;
public readonly string Name;
public readonly EntityUid? EntityUid;
public readonly HandLocation Location;
public SharedHand(int index, string name, EntityUid? entityUid, HandLocation location)
{
Index = index;
Name = name;
EntityUid = entityUid;
Location = location;
}
}
// The IDs of the items get synced over the network. // The IDs of the items get synced over the network.
[Serializable, NetSerializable] [Serializable, NetSerializable]
public class HandsComponentState : ComponentState public class HandsComponentState : ComponentState
{ {
public readonly Dictionary<string, EntityUid> Hands; public readonly SharedHand[] Hands;
public readonly string ActiveIndex; public readonly string ActiveIndex;
public HandsComponentState(Dictionary<string, EntityUid> hands, string activeIndex) : base(ContentNetIDs.HANDS) public HandsComponentState(SharedHand[] hands, string activeIndex) : base(ContentNetIDs.HANDS)
{ {
Hands = hands; Hands = hands;
ActiveIndex = activeIndex; ActiveIndex = activeIndex;
@@ -75,4 +94,11 @@ namespace Content.Shared.GameObjects
Index = index; Index = index;
} }
} }
public enum HandLocation : byte
{
Left,
Middle,
Right
}
} }

View File

@@ -86,6 +86,8 @@
- deleteewc - deleteewc
- asay - asay
- mapping - mapping
- addhand
- removehand
CanViewVar: true CanViewVar: true
CanAdminPlace: true CanAdminPlace: true
@@ -159,6 +161,8 @@
- sudo - sudo
- asay - asay
- mapping - mapping
- addhand
- removehand
CanViewVar: true CanViewVar: true
CanAdminPlace: true CanAdminPlace: true
CanScript: true CanScript: true

View File

@@ -10,9 +10,6 @@
- type: AiController - type: AiController
logic: Mimic logic: Mimic
- type: Hands - type: Hands
hands:
- left
- right
- type: MovementSpeedModifier - type: MovementSpeedModifier
- type: InteractionOutline - type: InteractionOutline
- type: Sprite - type: Sprite

View File

@@ -10,9 +10,6 @@
- type: AiController - type: AiController
logic: Xeno logic: Xeno
- type: Hands - type: Hands
hands:
- left
- right
- type: MovementSpeedModifier - type: MovementSpeedModifier
- type: InteractionOutline - type: InteractionOutline
- type: Sprite - type: Sprite

View File

@@ -10,9 +10,6 @@
components: components:
- type: Flashable - type: Flashable
- type: Hands - type: Hands
hands:
- left
- right
- type: MovementSpeedModifier - type: MovementSpeedModifier
- type: Hunger - type: Hunger
- type: Thirst - type: Thirst
@@ -150,10 +147,6 @@
description: A dummy human meant to be used in character setup description: A dummy human meant to be used in character setup
components: components:
- type: Hands - type: Hands
hands:
- left
- right
- type: Inventory - type: Inventory
- type: Sprite - type: Sprite
netsync: false netsync: false

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB