Improve hands & pulling (#4389)
This commit is contained in:
committed by
GitHub
parent
73e4946e27
commit
632e72b817
@@ -1,9 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.Actions.Assignments;
|
||||
using Content.Client.Actions.UI;
|
||||
using Content.Client.Hands;
|
||||
using Content.Client.Inventory;
|
||||
using Content.Client.Items.UI;
|
||||
using Content.Client.Items.Managers;
|
||||
using Content.Shared.Actions.Components;
|
||||
using Content.Shared.Actions.Prototypes;
|
||||
using Robust.Client.GameObjects;
|
||||
@@ -26,12 +25,13 @@ namespace Content.Client.Actions
|
||||
public const byte Slots = 10;
|
||||
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IItemSlotManager _itemSlotManager = default!;
|
||||
|
||||
[ComponentDependency] private readonly HandsComponent? _handsComponent = null;
|
||||
[ComponentDependency] private readonly ClientInventoryComponent? _inventoryComponent = null;
|
||||
|
||||
private ActionsUI? _ui;
|
||||
private readonly List<ItemSlotButton> _highlightingItemSlots = new();
|
||||
private EntityUid _highlightedEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Current assignments for all hotbars / slots for this entity.
|
||||
@@ -225,26 +225,8 @@ namespace Content.Client.Actions
|
||||
{
|
||||
StopHighlightingItemSlots();
|
||||
|
||||
// figure out if it's in hand or inventory and highlight it
|
||||
foreach (var hand in _handsComponent!.Gui!.Hands)
|
||||
{
|
||||
if (hand.HeldItem != item || hand.HandButton == null) continue;
|
||||
_highlightingItemSlots.Add(hand.HandButton);
|
||||
hand.HandButton.Highlight(true);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (slot, slotItem) in _inventoryComponent!.AllSlots)
|
||||
{
|
||||
if (slotItem != item) continue;
|
||||
foreach (var itemSlotButton in
|
||||
_inventoryComponent.InterfaceController.GetItemSlotButtons(slot))
|
||||
{
|
||||
_highlightingItemSlots.Add(itemSlotButton);
|
||||
itemSlotButton.Highlight(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
_highlightedEntity = item.Uid;
|
||||
_itemSlotManager.HighlightEntity(item.Uid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -252,11 +234,11 @@ namespace Content.Client.Actions
|
||||
/// </summary>
|
||||
public void StopHighlightingItemSlots()
|
||||
{
|
||||
foreach (var itemSlot in _highlightingItemSlots)
|
||||
{
|
||||
itemSlot.Highlight(false);
|
||||
}
|
||||
_highlightingItemSlots.Clear();
|
||||
if (_highlightedEntity == default)
|
||||
return;
|
||||
|
||||
_itemSlotManager.UnHighlightEntity(_highlightedEntity);
|
||||
_highlightedEntity = default;
|
||||
}
|
||||
|
||||
public void ToggleActionsMenu()
|
||||
|
||||
3
Content.Client/Hands/HandVirtualPullItemStatus.xaml
Normal file
3
Content.Client/Hands/HandVirtualPullItemStatus.xaml
Normal file
@@ -0,0 +1,3 @@
|
||||
<Control xmlns="https://spacestation14.io">
|
||||
<Label StyleClasses="ItemStatus" Text="Pulling" />
|
||||
</Control>
|
||||
13
Content.Client/Hands/HandVirtualPullItemStatus.xaml.cs
Normal file
13
Content.Client/Hands/HandVirtualPullItemStatus.xaml.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Hands
|
||||
{
|
||||
public sealed class HandVirtualPullItemStatus : Control
|
||||
{
|
||||
public HandVirtualPullItemStatus()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.Animations;
|
||||
using Content.Client.HUD;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Item;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.Hands
|
||||
{
|
||||
@@ -18,16 +11,7 @@ namespace Content.Client.Hands
|
||||
[ComponentReference(typeof(SharedHandsComponent))]
|
||||
public class HandsComponent : SharedHandsComponent
|
||||
{
|
||||
[Dependency] private readonly IGameHud _gameHud = default!;
|
||||
|
||||
[ViewVariables]
|
||||
public HandsGui? Gui { get; private set; }
|
||||
|
||||
protected override void OnRemove()
|
||||
{
|
||||
ClearGui();
|
||||
base.OnRemove();
|
||||
}
|
||||
public HandsGui? Gui { get; set; }
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
@@ -38,94 +22,23 @@ namespace Content.Client.Hands
|
||||
|
||||
foreach (var handState in state.Hands)
|
||||
{
|
||||
var newHand = new Hand(handState.Name, handState.Enabled, handState.Location);
|
||||
var newHand = new Hand(handState.Name, handState.Location);
|
||||
Hands.Add(newHand);
|
||||
}
|
||||
|
||||
ActiveHand = state.ActiveHand;
|
||||
|
||||
UpdateHandContainers();
|
||||
UpdateHandVisualizer();
|
||||
UpdateHandsGuiState();
|
||||
}
|
||||
|
||||
public void SettupGui()
|
||||
{
|
||||
if (Gui == null)
|
||||
{
|
||||
Gui = new HandsGui();
|
||||
_gameHud.HandsContainer.AddChild(Gui);
|
||||
Gui.HandClick += args => OnHandClick(args.HandClicked);
|
||||
Gui.HandActivate += args => OnActivateInHand(args.HandUsed);
|
||||
UpdateHandsGuiState();
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearGui()
|
||||
{
|
||||
Gui?.Dispose();
|
||||
Gui = null;
|
||||
}
|
||||
|
||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession? session = null)
|
||||
{
|
||||
base.HandleNetworkMessage(message, netChannel, session);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case PickupAnimationMessage msg:
|
||||
RunPickupAnimation(msg);
|
||||
break;
|
||||
}
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new HandsModifiedMessage { Hands = this });
|
||||
}
|
||||
|
||||
public override void HandsModified()
|
||||
{
|
||||
base.HandsModified();
|
||||
|
||||
UpdateHandContainers();
|
||||
UpdateHandVisualizer();
|
||||
UpdateHandsGuiState();
|
||||
}
|
||||
|
||||
private void OnHandClick(string handClicked)
|
||||
{
|
||||
if (!TryGetHand(handClicked, out var pressedHand))
|
||||
return;
|
||||
|
||||
if (!TryGetActiveHand(out var activeHand))
|
||||
return;
|
||||
|
||||
var pressedEntity = pressedHand.HeldEntity;
|
||||
var activeEntity = activeHand.HeldEntity;
|
||||
|
||||
if (pressedHand == activeHand && activeEntity != null)
|
||||
{
|
||||
SendNetworkMessage(new UseInHandMsg()); //use item in hand
|
||||
return;
|
||||
}
|
||||
|
||||
if (pressedHand != activeHand && pressedEntity == null)
|
||||
{
|
||||
SendNetworkMessage(new ClientChangedHandMsg(pressedHand.Name)); //swap hand
|
||||
return;
|
||||
}
|
||||
|
||||
if (pressedHand != activeHand && pressedEntity != null && activeEntity != null)
|
||||
{
|
||||
SendNetworkMessage(new ClientAttackByInHandMsg(pressedHand.Name)); //use active item on held item
|
||||
return;
|
||||
}
|
||||
|
||||
if (pressedHand != activeHand && pressedEntity != null && activeEntity == null)
|
||||
{
|
||||
SendNetworkMessage(new MoveItemFromHandMsg(pressedHand.Name)); //move item in hand to active hand
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnActivateInHand(string handActivated)
|
||||
{
|
||||
SendNetworkMessage(new ActivateInHandMsg(handActivated));
|
||||
base.HandsModified();
|
||||
}
|
||||
|
||||
public void UpdateHandContainers()
|
||||
@@ -149,27 +62,10 @@ namespace Content.Client.Hands
|
||||
appearance.SetData(HandsVisuals.VisualState, GetHandsVisualState());
|
||||
}
|
||||
|
||||
public void UpdateHandsGuiState()
|
||||
{
|
||||
Gui?.SetState(GetHandsGuiState());
|
||||
}
|
||||
|
||||
private HandsGuiState GetHandsGuiState()
|
||||
{
|
||||
var handStates = new List<GuiHand>();
|
||||
|
||||
foreach (var hand in ReadOnlyHands)
|
||||
{
|
||||
var handState = new GuiHand(hand.Name, hand.Location, hand.HeldEntity, hand.Enabled);
|
||||
handStates.Add(handState);
|
||||
}
|
||||
return new HandsGuiState(handStates, ActiveHand);
|
||||
}
|
||||
|
||||
private HandsVisualState GetHandsVisualState()
|
||||
{
|
||||
var hands = new List<HandVisualState>();
|
||||
foreach (var hand in ReadOnlyHands)
|
||||
foreach (var hand in Hands)
|
||||
{
|
||||
if (hand.HeldEntity == null)
|
||||
continue;
|
||||
@@ -182,16 +78,5 @@ namespace Content.Client.Hands
|
||||
}
|
||||
return new(hands);
|
||||
}
|
||||
|
||||
private void RunPickupAnimation(PickupAnimationMessage msg)
|
||||
{
|
||||
if (!Owner.EntityManager.TryGetEntity(msg.EntityUid, out var entity))
|
||||
return;
|
||||
|
||||
if (!IoCManager.Resolve<IGameTiming>().IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
ReusableAnimations.AnimateEntityPickup(entity, msg.InitialPosition, msg.PickupDirection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
6
Content.Client/Hands/HandsGui.xaml
Normal file
6
Content.Client/Hands/HandsGui.xaml
Normal file
@@ -0,0 +1,6 @@
|
||||
<Control xmlns="https://spacestation14.io">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<Control Name="StatusContainer" />
|
||||
<BoxContainer Name="HandsContainer" Orientation="Horizontal" HorizontalAlignment="Center" />
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
@@ -1,87 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Client.HUD;
|
||||
using Content.Client.Items.Managers;
|
||||
using Content.Client.Items.UI;
|
||||
using Content.Client.Resources;
|
||||
using Content.Shared;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Content.Client.Hands
|
||||
{
|
||||
public class HandsGui : Control
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class HandsGui : Control
|
||||
{
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly IItemSlotManager _itemSlotManager = default!;
|
||||
[Dependency] private readonly IGameHud _gameHud = default!;
|
||||
[Dependency] private readonly INetConfigurationManager _configManager = default!;
|
||||
|
||||
private readonly HandsSystem _handsSystem;
|
||||
private readonly HandsComponent _handsComponent;
|
||||
|
||||
private Texture StorageTexture => _gameHud.GetHudTexture("back.png");
|
||||
private Texture BlockedTexture => _resourceCache.GetTexture("/Textures/Interface/Inventory/blocked.png");
|
||||
|
||||
private ItemStatusPanel StatusPanel { get; }
|
||||
|
||||
private BoxContainer HandsContainer { get; }
|
||||
|
||||
[ViewVariables]
|
||||
public IReadOnlyList<GuiHand> Hands => _hands;
|
||||
private List<GuiHand> _hands = new();
|
||||
[ViewVariables] private GuiHand[] _hands = Array.Empty<GuiHand>();
|
||||
|
||||
private string? ActiveHand { get; set; }
|
||||
|
||||
public Action<HandClickEventArgs>? HandClick; //TODO: Move to Eventbus
|
||||
|
||||
public Action<HandActivateEventArgs>? HandActivate; //TODO: Move to Eventbus
|
||||
|
||||
public HandsGui()
|
||||
public HandsGui(HandsComponent hands, HandsSystem handsSystem)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_configManager.OnValueChanged(CCVars.HudTheme, UpdateHudTheme);
|
||||
_handsComponent = hands;
|
||||
_handsSystem = handsSystem;
|
||||
|
||||
AddChild(new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
SeparationOverride = 0,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
Children =
|
||||
{
|
||||
new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Children =
|
||||
{
|
||||
(StatusPanel = ItemStatusPanel.FromSide(HandLocation.Middle)),
|
||||
(HandsContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
HorizontalAlignment = HAlignment.Center
|
||||
}),
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
StatusPanel = ItemStatusPanel.FromSide(HandLocation.Middle);
|
||||
StatusContainer.AddChild(StatusPanel);
|
||||
StatusPanel.SetPositionFirst();
|
||||
}
|
||||
|
||||
public void SetState(HandsGuiState state)
|
||||
protected override void EnteredTree()
|
||||
{
|
||||
base.EnteredTree();
|
||||
|
||||
_handsSystem.GuiStateUpdated += HandsSystemOnGuiStateUpdated;
|
||||
_configManager.OnValueChanged(CCVars.HudTheme, UpdateHudTheme);
|
||||
|
||||
HandsSystemOnGuiStateUpdated();
|
||||
}
|
||||
|
||||
protected override void ExitedTree()
|
||||
{
|
||||
base.ExitedTree();
|
||||
|
||||
_handsSystem.GuiStateUpdated -= HandsSystemOnGuiStateUpdated;
|
||||
_configManager.UnsubValueChanged(CCVars.HudTheme, UpdateHudTheme);
|
||||
}
|
||||
|
||||
private void HandsSystemOnGuiStateUpdated()
|
||||
{
|
||||
var state = _handsSystem.GetGuiState();
|
||||
|
||||
ActiveHand = state.ActiveHand;
|
||||
_hands = state.GuiHands;
|
||||
Array.Sort(_hands, HandOrderComparer.Instance);
|
||||
UpdateGui();
|
||||
}
|
||||
|
||||
@@ -97,12 +94,15 @@ namespace Content.Client.Hands
|
||||
|
||||
var handName = hand.Name;
|
||||
newButton.OnPressed += args => OnHandPressed(args, handName);
|
||||
newButton.OnStoragePressed += args => OnStoragePressed(handName);
|
||||
|
||||
newButton.Blocked.Visible = !hand.Enabled;
|
||||
newButton.OnStoragePressed += _ => OnStoragePressed(handName);
|
||||
|
||||
_itemSlotManager.SetItemSlot(newButton, hand.HeldItem);
|
||||
|
||||
// Show blocked overlay if hand is pulling.
|
||||
newButton.Blocked.Visible =
|
||||
hand.HeldItem != null && hand.HeldItem.HasComponent<HandVirtualPullComponent>();
|
||||
}
|
||||
|
||||
if (TryGetActiveHand(out var activeHand))
|
||||
{
|
||||
activeHand.HandButton.SetActiveHand(true);
|
||||
@@ -114,7 +114,7 @@ namespace Content.Client.Hands
|
||||
{
|
||||
if (args.Function == EngineKeyFunctions.UIClick)
|
||||
{
|
||||
HandClick?.Invoke(new HandClickEventArgs(handName));
|
||||
_handsSystem.UIHandClick(_handsComponent, handName);
|
||||
}
|
||||
else if (TryGetHand(handName, out var hand))
|
||||
{
|
||||
@@ -124,7 +124,7 @@ namespace Content.Client.Hands
|
||||
|
||||
private void OnStoragePressed(string handName)
|
||||
{
|
||||
HandActivate?.Invoke(new HandActivateEventArgs(handName));
|
||||
_handsSystem.UIHandActivate(handName);
|
||||
}
|
||||
|
||||
private bool TryGetActiveHand([NotNullWhen(true)] out GuiHand? activeHand)
|
||||
@@ -145,6 +145,7 @@ namespace Content.Client.Hands
|
||||
if (hand.Name == handName)
|
||||
foundHand = hand;
|
||||
}
|
||||
|
||||
return foundHand != null;
|
||||
}
|
||||
|
||||
@@ -153,7 +154,9 @@ namespace Content.Client.Hands
|
||||
base.FrameUpdate(args);
|
||||
|
||||
foreach (var hand in _hands)
|
||||
{
|
||||
_itemSlotManager.UpdateCooldown(hand.HandButton, hand.HeldItem);
|
||||
}
|
||||
}
|
||||
|
||||
private HandButton MakeHandButton(HandLocation buttonLocation)
|
||||
@@ -173,23 +176,31 @@ namespace Content.Client.Hands
|
||||
UpdateGui();
|
||||
}
|
||||
|
||||
public class HandClickEventArgs
|
||||
private sealed class HandOrderComparer : IComparer<GuiHand>
|
||||
{
|
||||
public string HandClicked { get; }
|
||||
public static readonly HandOrderComparer Instance = new();
|
||||
|
||||
public HandClickEventArgs(string handClicked)
|
||||
public int Compare(GuiHand? x, GuiHand? y)
|
||||
{
|
||||
HandClicked = handClicked;
|
||||
}
|
||||
}
|
||||
if (ReferenceEquals(x, y)) return 0;
|
||||
if (ReferenceEquals(null, y)) return 1;
|
||||
if (ReferenceEquals(null, x)) return -1;
|
||||
|
||||
public class HandActivateEventArgs
|
||||
{
|
||||
public string HandUsed { get; }
|
||||
var orderX = Map(x.HandLocation);
|
||||
var orderY = Map(y.HandLocation);
|
||||
|
||||
public HandActivateEventArgs(string handUsed)
|
||||
{
|
||||
HandUsed = handUsed;
|
||||
return orderX.CompareTo(orderY);
|
||||
|
||||
static int Map(HandLocation loc)
|
||||
{
|
||||
return loc switch
|
||||
{
|
||||
HandLocation.Left => 3,
|
||||
HandLocation.Middle => 2,
|
||||
HandLocation.Right => 1,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(loc), loc, null)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,7 +214,7 @@ namespace Content.Client.Hands
|
||||
/// The set of hands to be displayed.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public List<GuiHand> GuiHands { get; } = new();
|
||||
public GuiHand[] GuiHands { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the currently active hand.
|
||||
@@ -211,7 +222,7 @@ namespace Content.Client.Hands
|
||||
[ViewVariables]
|
||||
public string? ActiveHand { get; }
|
||||
|
||||
public HandsGuiState(List<GuiHand> guiHands, string? activeHand = null)
|
||||
public HandsGuiState(GuiHand[] guiHands, string? activeHand = null)
|
||||
{
|
||||
GuiHands = guiHands;
|
||||
ActiveHand = activeHand;
|
||||
@@ -247,18 +258,11 @@ namespace Content.Client.Hands
|
||||
[ViewVariables]
|
||||
public HandButton HandButton { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// If this hand can be used by the player.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool Enabled { get; }
|
||||
|
||||
public GuiHand(string name, HandLocation handLocation, IEntity? heldItem, bool enabled)
|
||||
public GuiHand(string name, HandLocation handLocation, IEntity? heldItem)
|
||||
{
|
||||
Name = name;
|
||||
HandLocation = handLocation;
|
||||
HeldItem = heldItem;
|
||||
Enabled = enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Players;
|
||||
|
||||
namespace Content.Client.Hands
|
||||
{
|
||||
internal sealed class HandsSystem : SharedHandsSystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<HandsComponent, PlayerAttachedEvent>((_, component, _) => component.SettupGui());
|
||||
SubscribeLocalEvent<HandsComponent, PlayerDetachedEvent>((_, component, _) => component.ClearGui());
|
||||
|
||||
CommandBinds.Builder
|
||||
.Bind(ContentKeyFunctions.SwapHands, InputCmdHandler.FromDelegate(SwapHandsPressed))
|
||||
.Bind(ContentKeyFunctions.Drop, new PointerInputCmdHandler(DropPressed))
|
||||
.Register<HandsSystem>();
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
CommandBinds.Unregister<HandsSystem>();
|
||||
base.Shutdown();
|
||||
}
|
||||
|
||||
private void SwapHandsPressed(ICommonSession? session)
|
||||
{
|
||||
if (session == null)
|
||||
return;
|
||||
|
||||
var player = session.AttachedEntity;
|
||||
|
||||
if (player == null)
|
||||
return;
|
||||
|
||||
if (!player.TryGetComponent(out SharedHandsComponent? hands))
|
||||
return;
|
||||
|
||||
if (!hands.TryGetSwapHandsResult(out var nextHand))
|
||||
return;
|
||||
|
||||
EntityManager.RaisePredictiveEvent(new RequestSetHandEvent(nextHand));
|
||||
}
|
||||
|
||||
private bool DropPressed(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
|
||||
{
|
||||
if (session == null)
|
||||
return false;
|
||||
|
||||
var player = session.AttachedEntity;
|
||||
|
||||
if (player == null)
|
||||
return false;
|
||||
|
||||
if (!player.TryGetComponent(out SharedHandsComponent? hands))
|
||||
return false;
|
||||
|
||||
var activeHand = hands.ActiveHand;
|
||||
|
||||
if (activeHand == null)
|
||||
return false;
|
||||
|
||||
EntityManager.RaisePredictiveEvent(new RequestDropHeldEntityEvent(activeHand, coords));
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void HandleContainerModified(EntityUid uid, SharedHandsComponent component, ContainerModifiedMessage args)
|
||||
{
|
||||
component.HandsModified();
|
||||
}
|
||||
}
|
||||
}
|
||||
18
Content.Client/Hands/Systems/HandVirtualPullSystem.cs
Normal file
18
Content.Client/Hands/Systems/HandVirtualPullSystem.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using Content.Client.Items;
|
||||
using Content.Shared.Hands.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.Hands
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class HandVirtualPullSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
Subs.ItemStatus<HandVirtualPullComponent>(_ => new HandVirtualPullItemStatus());
|
||||
}
|
||||
}
|
||||
}
|
||||
149
Content.Client/Hands/Systems/HandsSystem.cs
Normal file
149
Content.Client/Hands/Systems/HandsSystem.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Content.Client.Animations;
|
||||
using Content.Client.HUD;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Hands
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class HandsSystem : SharedHandsSystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IGameHud _gameHud = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
public event Action? GuiStateUpdated;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<HandsComponent, PlayerAttachedEvent>(HandlePlayerAttached);
|
||||
SubscribeLocalEvent<HandsComponent, PlayerDetachedEvent>(HandlePlayerDetached);
|
||||
SubscribeLocalEvent<HandsComponent, ComponentRemove>(HandleCompRemove);
|
||||
SubscribeLocalEvent<HandsModifiedMessage>(HandleHandsModified);
|
||||
|
||||
SubscribeNetworkEvent<PickupAnimationMessage>(HandlePickupAnimation);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
CommandBinds.Unregister<HandsSystem>();
|
||||
base.Shutdown();
|
||||
}
|
||||
|
||||
private void HandleHandsModified(HandsModifiedMessage ev)
|
||||
{
|
||||
if (ev.Hands.Owner == _playerManager.LocalPlayer?.ControlledEntity)
|
||||
GuiStateUpdated?.Invoke();
|
||||
}
|
||||
|
||||
protected override void HandleContainerModified(EntityUid uid, SharedHandsComponent component, ContainerModifiedMessage args)
|
||||
{
|
||||
if (uid == _playerManager.LocalPlayer?.ControlledEntity?.Uid)
|
||||
GuiStateUpdated?.Invoke();
|
||||
}
|
||||
|
||||
private void HandlePickupAnimation(PickupAnimationMessage msg)
|
||||
{
|
||||
if (!EntityManager.TryGetEntity(msg.EntityUid, out var entity))
|
||||
return;
|
||||
|
||||
if (!_gameTiming.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
ReusableAnimations.AnimateEntityPickup(entity, msg.InitialPosition, msg.PickupDirection);
|
||||
}
|
||||
|
||||
public HandsGuiState GetGuiState()
|
||||
{
|
||||
var player = _playerManager.LocalPlayer?.ControlledEntity;
|
||||
|
||||
if (player == null || !player.TryGetComponent(out HandsComponent? hands))
|
||||
return new HandsGuiState(Array.Empty<GuiHand>());
|
||||
|
||||
var states = hands.Hands
|
||||
.Select(hand => new GuiHand(hand.Name, hand.Location, hand.HeldEntity))
|
||||
.ToArray();
|
||||
|
||||
return new HandsGuiState(states, hands.ActiveHand);
|
||||
}
|
||||
|
||||
public void UIHandClick(HandsComponent hands, string handName)
|
||||
{
|
||||
if (!hands.TryGetHand(handName, out var pressedHand))
|
||||
return;
|
||||
|
||||
if (!hands.TryGetActiveHand(out var activeHand))
|
||||
return;
|
||||
|
||||
var pressedEntity = pressedHand.HeldEntity;
|
||||
var activeEntity = activeHand.HeldEntity;
|
||||
|
||||
if (pressedHand == activeHand && activeEntity != null)
|
||||
{
|
||||
// use item in hand
|
||||
// it will always be attack_self() in my heart.
|
||||
RaiseNetworkEvent(new UseInHandMsg());
|
||||
return;
|
||||
}
|
||||
|
||||
if (pressedHand != activeHand && pressedEntity == null)
|
||||
{
|
||||
// change active hand
|
||||
RaiseNetworkEvent(new RequestSetHandEvent(handName));
|
||||
return;
|
||||
}
|
||||
|
||||
if (pressedHand != activeHand && pressedEntity != null && activeEntity != null)
|
||||
{
|
||||
// use active item on held item
|
||||
RaiseNetworkEvent(new ClientInteractUsingInHandMsg(pressedHand.Name));
|
||||
return;
|
||||
}
|
||||
|
||||
if (pressedHand != activeHand && pressedEntity != null && activeEntity == null)
|
||||
{
|
||||
// use active item on held item
|
||||
RaiseNetworkEvent(new MoveItemFromHandMsg(pressedHand.Name));
|
||||
}
|
||||
}
|
||||
|
||||
public void UIHandActivate(string handName)
|
||||
{
|
||||
RaiseNetworkEvent (new ActivateInHandMsg(handName));
|
||||
}
|
||||
|
||||
private void HandlePlayerAttached(EntityUid uid, HandsComponent component, PlayerAttachedEvent args)
|
||||
{
|
||||
component.Gui = new HandsGui(component, this);
|
||||
_gameHud.HandsContainer.AddChild(component.Gui);
|
||||
}
|
||||
|
||||
private static void HandlePlayerDetached(EntityUid uid, HandsComponent component, PlayerDetachedEvent args)
|
||||
{
|
||||
ClearGui(component);
|
||||
}
|
||||
|
||||
private static void HandleCompRemove(EntityUid uid, HandsComponent component, ComponentRemove args)
|
||||
{
|
||||
ClearGui(component);
|
||||
}
|
||||
|
||||
private static void ClearGui(HandsComponent comp)
|
||||
{
|
||||
comp.Gui?.Orphan();
|
||||
comp.Gui = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Content.Client/Items/ItemStatusMessages.cs
Normal file
32
Content.Client/Items/ItemStatusMessages.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.Items
|
||||
{
|
||||
public sealed class ItemStatusCollectMessage : EntityEventArgs
|
||||
{
|
||||
public List<Control> Controls = new();
|
||||
}
|
||||
|
||||
public static class ItemStatusRegisterExt
|
||||
{
|
||||
/// <summary>
|
||||
/// Register an item status control for a component.
|
||||
/// </summary>
|
||||
/// <param name="subs">The <see cref="EntitySystem.Subs"/> handle from within entity system initialize.</param>
|
||||
/// <param name="createControl">A delegate to create the actual control.</param>
|
||||
/// <typeparam name="TComp">The type of component for which this control should be made.</typeparam>
|
||||
public static void ItemStatus<TComp>(
|
||||
this EntitySystem.Subscriptions subs,
|
||||
Func<EntityUid, Control> createControl)
|
||||
where TComp : IComponent
|
||||
{
|
||||
subs.SubscribeLocalEvent<TComp, ItemStatusCollectMessage>((uid, _, args) =>
|
||||
{
|
||||
args.Controls.Add(createControl(uid));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Client.Items.UI;
|
||||
using System;
|
||||
using Content.Client.Items.UI;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
@@ -10,5 +11,21 @@ namespace Content.Client.Items.Managers
|
||||
void UpdateCooldown(ItemSlotButton? cooldownTexture, IEntity? entity);
|
||||
bool SetItemSlot(ItemSlotButton button, IEntity? entity);
|
||||
void HoverInSlot(ItemSlotButton button, IEntity? entity, bool fits);
|
||||
event Action<EntitySlotHighlightedEventArgs>? EntityHighlightedUpdated;
|
||||
bool IsHighlighted(EntityUid uid);
|
||||
|
||||
/// <summary>
|
||||
/// Highlight all slot controls that contain the specified entity.
|
||||
/// </summary>
|
||||
/// <param name="uid">The UID of the entity to highlight.</param>
|
||||
/// <seealso cref="UnHighlightEntity"/>
|
||||
void HighlightEntity(EntityUid uid);
|
||||
|
||||
/// <summary>
|
||||
/// Remove highlighting for the specified entity.
|
||||
/// </summary>
|
||||
/// <param name="uid">The UID of the entity to unhighlight.</param>
|
||||
/// <seealso cref="HighlightEntity"/>
|
||||
void UnHighlightEntity(EntityUid uid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.Examine;
|
||||
using Content.Client.Items.UI;
|
||||
using Content.Client.Storage;
|
||||
using Content.Client.Verbs;
|
||||
using Content.Shared.Cooldown;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
@@ -28,6 +31,11 @@ namespace Content.Client.Items.Managers
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IComponentManager _componentManager = default!;
|
||||
|
||||
private readonly HashSet<EntityUid> _highlightEntities = new();
|
||||
|
||||
public event Action<EntitySlotHighlightedEventArgs>? EntityHighlightedUpdated;
|
||||
|
||||
public bool SetItemSlot(ItemSlotButton button, IEntity? entity)
|
||||
{
|
||||
@@ -38,13 +46,26 @@ namespace Content.Client.Items.Managers
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!entity.TryGetComponent(out ISpriteComponent? sprite))
|
||||
ISpriteComponent? sprite;
|
||||
if (entity.TryGetComponent(out HandVirtualPullComponent? virtPull)
|
||||
&& _componentManager.TryGetComponent(virtPull.PulledEntity, out ISpriteComponent pulledSprite))
|
||||
{
|
||||
sprite = pulledSprite;
|
||||
}
|
||||
else if (!entity.TryGetComponent(out sprite))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
button.ClearHover();
|
||||
button.SpriteView.Sprite = sprite;
|
||||
button.StorageButton.Visible = entity.HasComponent<ClientStorageComponent>();
|
||||
}
|
||||
|
||||
button.Entity = entity?.Uid ?? default;
|
||||
|
||||
// im lazy
|
||||
button.UpdateSlotHighlighted();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -145,5 +166,38 @@ namespace Content.Client.Items.Managers
|
||||
|
||||
button.HoverSpriteView.Sprite = hoverSprite;
|
||||
}
|
||||
|
||||
public bool IsHighlighted(EntityUid uid)
|
||||
{
|
||||
return _highlightEntities.Contains(uid);
|
||||
}
|
||||
|
||||
public void HighlightEntity(EntityUid uid)
|
||||
{
|
||||
if (!_highlightEntities.Add(uid))
|
||||
return;
|
||||
|
||||
EntityHighlightedUpdated?.Invoke(new EntitySlotHighlightedEventArgs(uid, true));
|
||||
}
|
||||
|
||||
public void UnHighlightEntity(EntityUid uid)
|
||||
{
|
||||
if (!_highlightEntities.Remove(uid))
|
||||
return;
|
||||
|
||||
EntityHighlightedUpdated?.Invoke(new EntitySlotHighlightedEventArgs(uid, false));
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct EntitySlotHighlightedEventArgs
|
||||
{
|
||||
public EntitySlotHighlightedEventArgs(EntityUid entity, bool newHighlighted)
|
||||
{
|
||||
Entity = entity;
|
||||
NewHighlighted = newHighlighted;
|
||||
}
|
||||
|
||||
public EntityUid Entity { get; }
|
||||
public bool NewHighlighted { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
using System;
|
||||
using Content.Client.Cooldown;
|
||||
using Content.Client.Items.Managers;
|
||||
using Content.Client.Stylesheets;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.Items.UI
|
||||
{
|
||||
public class ItemSlotButton : Control
|
||||
public class ItemSlotButton : Control, IEntityEventSubscriber
|
||||
{
|
||||
private const string HighlightShader = "SelectionOutlineInrange";
|
||||
|
||||
[Dependency] private readonly IItemSlotManager _itemSlotManager = default!;
|
||||
|
||||
public EntityUid Entity { get; set; }
|
||||
public TextureRect Button { get; }
|
||||
public SpriteView SpriteView { get; }
|
||||
public SpriteView HoverSpriteView { get; }
|
||||
@@ -32,6 +38,8 @@ namespace Content.Client.Items.UI
|
||||
|
||||
public ItemSlotButton(Texture texture, Texture storageTexture, string textureName)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
MinSize = (64, 64);
|
||||
|
||||
TextureName = textureName;
|
||||
@@ -101,6 +109,31 @@ namespace Content.Client.Items.UI
|
||||
});
|
||||
}
|
||||
|
||||
protected override void EnteredTree()
|
||||
{
|
||||
base.EnteredTree();
|
||||
|
||||
_itemSlotManager.EntityHighlightedUpdated += HandleEntitySlotHighlighted;
|
||||
UpdateSlotHighlighted();
|
||||
}
|
||||
|
||||
protected override void ExitedTree()
|
||||
{
|
||||
base.ExitedTree();
|
||||
|
||||
_itemSlotManager.EntityHighlightedUpdated -= HandleEntitySlotHighlighted;
|
||||
}
|
||||
|
||||
private void HandleEntitySlotHighlighted(EntitySlotHighlightedEventArgs entitySlotHighlightedEventArgs)
|
||||
{
|
||||
UpdateSlotHighlighted();
|
||||
}
|
||||
|
||||
public void UpdateSlotHighlighted()
|
||||
{
|
||||
Highlight(_itemSlotManager.IsHighlighted(Entity));
|
||||
}
|
||||
|
||||
public void ClearHover()
|
||||
{
|
||||
if (EntityHover)
|
||||
|
||||
@@ -8,7 +8,9 @@ using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Content.Client.IoC.StaticIoC;
|
||||
@@ -18,6 +20,8 @@ namespace Content.Client.Items.UI
|
||||
{
|
||||
public class ItemStatusPanel : Control
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
[ViewVariables]
|
||||
private readonly List<(IItemStatus, Control)> _activeStatusComponents = new();
|
||||
|
||||
@@ -33,6 +37,8 @@ namespace Content.Client.Items.UI
|
||||
|
||||
public ItemStatusPanel(Texture texture, StyleBox.Margin cutout, StyleBox.Margin flat, Label.AlignMode textAlign)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
var panel = new StyleBoxTexture
|
||||
{
|
||||
Texture = texture
|
||||
@@ -117,6 +123,13 @@ namespace Content.Client.Items.UI
|
||||
return new ItemStatusPanel(ResC.GetTexture(texture), cutOut, flat, textAlign);
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
UpdateItemName();
|
||||
}
|
||||
|
||||
public void Update(IEntity? entity)
|
||||
{
|
||||
if (entity == null)
|
||||
@@ -131,12 +144,29 @@ namespace Content.Client.Items.UI
|
||||
{
|
||||
_entity = entity;
|
||||
BuildNewEntityStatus();
|
||||
_itemNameLabel.Text = entity.Name;
|
||||
|
||||
UpdateItemName();
|
||||
}
|
||||
|
||||
_panel.Visible = true;
|
||||
}
|
||||
|
||||
private void UpdateItemName()
|
||||
{
|
||||
if (_entity == null)
|
||||
return;
|
||||
|
||||
if (_entity.TryGetComponent(out HandVirtualPullComponent? virtualPull)
|
||||
&& _entityManager.TryGetEntity(virtualPull.PulledEntity, out var pulledEnt))
|
||||
{
|
||||
_itemNameLabel.Text = pulledEnt.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
_itemNameLabel.Text = _entity.Name;
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearOldStatus()
|
||||
{
|
||||
_statusContents.RemoveAllChildren();
|
||||
@@ -162,6 +192,14 @@ namespace Content.Client.Items.UI
|
||||
|
||||
_activeStatusComponents.Add((statusComponent, control));
|
||||
}
|
||||
|
||||
var collectMsg = new ItemStatusCollectMessage();
|
||||
_entity.EntityManager.EventBus.RaiseLocalEvent(_entity.Uid, collectMsg);
|
||||
|
||||
foreach (var control in collectMsg.Controls)
|
||||
{
|
||||
_statusContents.AddChild(control);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution.Components;
|
||||
using Content.Shared.DragDrop;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Content.Shared.Verbs;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
@@ -10,7 +11,6 @@ using Content.Shared.Audio;
|
||||
using Content.Shared.Body.Part;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Content.Shared.Physics.Pull;
|
||||
using Content.Shared.Pulling.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
@@ -19,9 +19,7 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Players;
|
||||
|
||||
namespace Content.Server.Hands.Components
|
||||
{
|
||||
@@ -35,48 +33,6 @@ namespace Content.Server.Hands.Components
|
||||
|
||||
int IDisarmedAct.Priority => int.MaxValue; // We want this to be the last disarm act to run.
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
||||
{
|
||||
base.HandleMessage(message, component);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case PullAttemptMessage msg:
|
||||
AttemptPull(msg);
|
||||
break;
|
||||
case PullStartedMessage:
|
||||
StartPulling();
|
||||
break;
|
||||
case PullStoppedMessage:
|
||||
StopPulling();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession? session = null)
|
||||
{
|
||||
base.HandleNetworkMessage(message, channel, session);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case ClientChangedHandMsg msg:
|
||||
ActiveHand = msg.HandName;
|
||||
break;
|
||||
case ClientAttackByInHandMsg msg:
|
||||
InteractHandWithActiveHand(msg.HandName);
|
||||
break;
|
||||
case UseInHandMsg:
|
||||
UseActiveHeldEntity();
|
||||
break;
|
||||
case ActivateInHandMsg msg:
|
||||
ActivateHeldEntity(msg.HandName);
|
||||
break;
|
||||
case MoveItemFromHandMsg msg:
|
||||
TryMoveHeldEntityToActiveHand(msg.HandName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnHeldEntityRemovedFromHand(IEntity heldEntity, HandState handState)
|
||||
{
|
||||
if (heldEntity.TryGetComponent(out ItemComponent? item))
|
||||
@@ -141,7 +97,8 @@ namespace Content.Server.Hands.Components
|
||||
if (pickupDirection == initialPosition.ToMapPos(Owner.EntityManager))
|
||||
return;
|
||||
|
||||
SendNetworkMessage(new PickupAnimationMessage(entity.Uid, pickupDirection, initialPosition));
|
||||
Owner.EntityManager.EntityNetManager!.SendSystemNetworkMessage(
|
||||
new PickupAnimationMessage(entity.Uid, pickupDirection, initialPosition));
|
||||
}
|
||||
|
||||
#region Pull/Disarm
|
||||
@@ -151,9 +108,17 @@ namespace Content.Server.Hands.Components
|
||||
if (args.Part.PartType != BodyPartType.Hand)
|
||||
return;
|
||||
|
||||
var handLocation = ReadOnlyHands.Count == 0 ? HandLocation.Right : HandLocation.Left; //TODO: make hand body part have a handlocation?
|
||||
// If this annoys you, which it should.
|
||||
// Ping Smugleaf.
|
||||
var location = args.Part.Symmetry switch
|
||||
{
|
||||
BodyPartSymmetry.None => HandLocation.Middle,
|
||||
BodyPartSymmetry.Left => HandLocation.Left,
|
||||
BodyPartSymmetry.Right => HandLocation.Right,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
AddHand(args.Slot, handLocation);
|
||||
AddHand(args.Slot, location);
|
||||
}
|
||||
|
||||
void IBodyPartRemoved.BodyPartRemoved(BodyPartRemovedEventArgs args)
|
||||
@@ -205,41 +170,13 @@ namespace Content.Server.Hands.Components
|
||||
return pullable.TryStopPull();
|
||||
}
|
||||
|
||||
private void AttemptPull(PullAttemptMessage msg)
|
||||
{
|
||||
if (!ReadOnlyHands.Any(hand => hand.Enabled))
|
||||
{
|
||||
msg.Cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void StartPulling()
|
||||
{
|
||||
var firstFreeHand = Hands.FirstOrDefault(hand => hand.Enabled);
|
||||
|
||||
if (firstFreeHand == null)
|
||||
return;
|
||||
|
||||
DisableHand(firstFreeHand);
|
||||
}
|
||||
|
||||
private void StopPulling()
|
||||
{
|
||||
var firstOccupiedHand = Hands.FirstOrDefault(hand => !hand.Enabled);
|
||||
|
||||
if (firstOccupiedHand == null)
|
||||
return;
|
||||
|
||||
EnableHand(firstOccupiedHand);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Old public methods
|
||||
|
||||
public IEnumerable<string> HandNames => ReadOnlyHands.Select(h => h.Name);
|
||||
public IEnumerable<string> HandNames => Hands.Select(h => h.Name);
|
||||
|
||||
public int Count => ReadOnlyHands.Count;
|
||||
public int Count => Hands.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of all hand names, with the active hand being first.
|
||||
@@ -249,9 +186,9 @@ namespace Content.Server.Hands.Components
|
||||
if (ActiveHand != null)
|
||||
yield return ActiveHand;
|
||||
|
||||
foreach (var hand in ReadOnlyHands)
|
||||
foreach (var hand in Hands)
|
||||
{
|
||||
if (hand.Name == ActiveHand || !hand.Enabled)
|
||||
if (hand.Name == ActiveHand)
|
||||
continue;
|
||||
|
||||
yield return hand.Name;
|
||||
|
||||
57
Content.Server/Hands/Systems/HandVirtualPullSystem.cs
Normal file
57
Content.Server/Hands/Systems/HandVirtualPullSystem.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using Content.Server.Pulling;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.Hands
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class HandVirtualPullSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<HandVirtualPullComponent, DroppedEvent>(HandlePullerDropped);
|
||||
SubscribeLocalEvent<HandVirtualPullComponent, UnequippedHandEvent>(HandlePullerUnequipped);
|
||||
|
||||
SubscribeLocalEvent<HandVirtualPullComponent, BeforeInteractEvent>(HandleBeforeInteract);
|
||||
}
|
||||
|
||||
private static void HandleBeforeInteract(
|
||||
EntityUid uid,
|
||||
HandVirtualPullComponent component,
|
||||
BeforeInteractEvent args)
|
||||
{
|
||||
// No interactions with a virtual pull, please.
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
// If the virtual pull gets removed from the hands for any reason, cancel the pull and delete it.
|
||||
private void HandlePullerUnequipped(EntityUid uid, HandVirtualPullComponent component, UnequippedHandEvent args)
|
||||
{
|
||||
MaybeDelete(component, args.User);
|
||||
}
|
||||
|
||||
private void HandlePullerDropped(EntityUid uid, HandVirtualPullComponent component, DroppedEvent args)
|
||||
{
|
||||
MaybeDelete(component, args.User);
|
||||
}
|
||||
|
||||
private void MaybeDelete(HandVirtualPullComponent comp, IEntity? user)
|
||||
{
|
||||
var pulled = comp.PulledEntity;
|
||||
|
||||
if (!ComponentManager.TryGetComponent(pulled, out PullableComponent? pullable))
|
||||
return;
|
||||
|
||||
if (pullable.Puller != user)
|
||||
return;
|
||||
|
||||
pullable.TryStopPull(user);
|
||||
comp.Owner.QueueDelete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,9 +13,9 @@ using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Input;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Content.Shared.Physics.Pull;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -23,6 +23,7 @@ using Robust.Shared.Localization;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Utility;
|
||||
using static Content.Shared.Inventory.EquipmentSlotDefines;
|
||||
|
||||
namespace Content.Server.Hands
|
||||
@@ -38,15 +39,126 @@ namespace Content.Server.Hands
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<HandsComponent, ExaminedEvent>(HandleExamined);
|
||||
SubscribeNetworkEvent<ActivateInHandMsg>(HandleActivateInHand);
|
||||
SubscribeNetworkEvent<ClientInteractUsingInHandMsg>(HandleInteractUsingInHand);
|
||||
SubscribeNetworkEvent<UseInHandMsg>(HandleUseInHand);
|
||||
SubscribeNetworkEvent<MoveItemFromHandMsg>(HandleMoveItemFromHand);
|
||||
|
||||
SubscribeLocalEvent<HandsComponent, PullAttemptMessage>(HandlePullAttempt);
|
||||
SubscribeLocalEvent<HandsComponent, PullStartedMessage>(HandlePullStarted);
|
||||
SubscribeLocalEvent<HandsComponent, PullStoppedMessage>(HandlePullStopped);
|
||||
|
||||
CommandBinds.Builder
|
||||
.Bind(ContentKeyFunctions.ActivateItemInHand, InputCmdHandler.FromDelegate(HandleActivateItem))
|
||||
.Bind(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem))
|
||||
.Bind(ContentKeyFunctions.SmartEquipBackpack, InputCmdHandler.FromDelegate(HandleSmartEquipBackpack))
|
||||
.Bind(ContentKeyFunctions.SmartEquipBelt, InputCmdHandler.FromDelegate(HandleSmartEquipBelt))
|
||||
.Bind(ContentKeyFunctions.SwapHands, InputCmdHandler.FromDelegate(SwapHandsPressed, handle: false))
|
||||
.Bind(ContentKeyFunctions.Drop, new PointerInputCmdHandler(DropPressed))
|
||||
.Register<HandsSystem>();
|
||||
}
|
||||
|
||||
private static void HandlePullAttempt(EntityUid uid, HandsComponent component, PullAttemptMessage args)
|
||||
{
|
||||
// Cancel pull if all hands full.
|
||||
if (component.Hands.All(hand => !hand.IsEmpty))
|
||||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
private void HandlePullStarted(EntityUid uid, HandsComponent component, PullStartedMessage args)
|
||||
{
|
||||
foreach (var handName in component.ActivePriorityEnumerable())
|
||||
{
|
||||
var hand = component.GetHand(handName);
|
||||
if (!hand.IsEmpty)
|
||||
continue;
|
||||
|
||||
var pos = component.Owner.Transform.Coordinates;
|
||||
var virtualPull = EntityManager.SpawnEntity("HandVirtualPull", pos);
|
||||
var virtualPullComp = virtualPull.GetComponent<HandVirtualPullComponent>();
|
||||
virtualPullComp.PulledEntity = args.Pulled.Owner.Uid;
|
||||
component.PutEntityIntoHand(hand, virtualPull);
|
||||
return;
|
||||
}
|
||||
|
||||
DebugTools.Assert("Unable to find available hand when starting pulling??");
|
||||
}
|
||||
|
||||
private void HandlePullStopped(EntityUid uid, HandsComponent component, PullStoppedMessage args)
|
||||
{
|
||||
// Try find hand that is doing this pull.
|
||||
// and clear it.
|
||||
foreach (var hand in component.Hands)
|
||||
{
|
||||
if (hand.HeldEntity == null
|
||||
|| !hand.HeldEntity.TryGetComponent(out HandVirtualPullComponent? virtualPull)
|
||||
|| virtualPull.PulledEntity != args.Pulled.Owner.Uid)
|
||||
continue;
|
||||
|
||||
hand.HeldEntity.Delete();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void SwapHandsPressed(ICommonSession? session)
|
||||
{
|
||||
var player = session?.AttachedEntity;
|
||||
|
||||
if (player == null)
|
||||
return;
|
||||
|
||||
if (!player.TryGetComponent(out SharedHandsComponent? hands))
|
||||
return;
|
||||
|
||||
if (!hands.TryGetSwapHandsResult(out var nextHand))
|
||||
return;
|
||||
|
||||
hands.ActiveHand = nextHand;
|
||||
}
|
||||
|
||||
private bool DropPressed(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
|
||||
{
|
||||
var player = session?.AttachedEntity;
|
||||
|
||||
if (player == null)
|
||||
return false;
|
||||
|
||||
if (!player.TryGetComponent(out SharedHandsComponent? hands))
|
||||
return false;
|
||||
|
||||
var activeHand = hands.ActiveHand;
|
||||
|
||||
if (activeHand == null)
|
||||
return false;
|
||||
|
||||
hands.TryDropHand(activeHand, coords);
|
||||
return false;
|
||||
}
|
||||
|
||||
private void HandleMoveItemFromHand(MoveItemFromHandMsg msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (!TryGetHandsComp(args.SenderSession, out var hands))
|
||||
return;
|
||||
|
||||
hands.TryMoveHeldEntityToActiveHand(msg.HandName);
|
||||
}
|
||||
|
||||
private void HandleUseInHand(UseInHandMsg msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (!TryGetHandsComp(args.SenderSession, out var hands))
|
||||
return;
|
||||
|
||||
hands.UseActiveHeldEntity();
|
||||
}
|
||||
|
||||
private void HandleInteractUsingInHand(ClientInteractUsingInHandMsg msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (!TryGetHandsComp(args.SenderSession, out var hands))
|
||||
return;
|
||||
|
||||
hands.InteractHandWithActiveHand(msg.HandName);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
@@ -54,6 +166,14 @@ namespace Content.Server.Hands
|
||||
CommandBinds.Unregister<HandsSystem>();
|
||||
}
|
||||
|
||||
private void HandleActivateInHand(ActivateInHandMsg msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (!TryGetHandsComp(args.SenderSession, out var hands))
|
||||
return;
|
||||
|
||||
hands.ActivateHeldEntity(msg.HandName);
|
||||
}
|
||||
|
||||
protected override void DropAllItemsInHands(IEntity entity, bool doMobChecks = true)
|
||||
{
|
||||
base.DropAllItemsInHands(entity, doMobChecks);
|
||||
@@ -75,25 +195,21 @@ namespace Content.Server.Hands
|
||||
}
|
||||
}
|
||||
|
||||
protected override void HandleContainerModified(EntityUid uid, SharedHandsComponent component, ContainerModifiedMessage args)
|
||||
{
|
||||
component.Dirty();
|
||||
}
|
||||
|
||||
private bool TryGetHandsComp(ICommonSession? session, [NotNullWhen(true)] out SharedHandsComponent? hands)
|
||||
private static bool TryGetHandsComp(
|
||||
ICommonSession? session,
|
||||
[NotNullWhen(true)] out SharedHandsComponent? hands)
|
||||
{
|
||||
hands = default;
|
||||
|
||||
if (session is not IPlayerSession playerSession)
|
||||
return false;
|
||||
|
||||
var playerEnt = playerSession?.AttachedEntity;
|
||||
var playerEnt = playerSession.AttachedEntity;
|
||||
|
||||
if (playerEnt == null || !playerEnt.IsValid())
|
||||
return false;
|
||||
|
||||
playerEnt.TryGetComponent(out hands);
|
||||
return hands != null;
|
||||
return playerEnt.TryGetComponent(out hands);
|
||||
}
|
||||
|
||||
private void HandleActivateItem(ICommonSession? session)
|
||||
@@ -387,6 +387,18 @@ namespace Content.Server.Interaction
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<bool> InteractDoBefore(
|
||||
IEntity user,
|
||||
IEntity used,
|
||||
IEntity? target,
|
||||
EntityCoordinates clickLocation,
|
||||
bool canReach)
|
||||
{
|
||||
var ev = new BeforeInteractEvent(user, used, target, clickLocation, canReach);
|
||||
RaiseLocalEvent(used.Uid, ev, false);
|
||||
return ev.Handled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses a item/object on an entity
|
||||
/// Finds components with the InteractUsing interface and calls their function
|
||||
@@ -397,6 +409,9 @@ namespace Content.Server.Interaction
|
||||
if (!_actionBlockerSystem.CanInteract(user))
|
||||
return;
|
||||
|
||||
if (await InteractDoBefore(user, used, target, clickLocation, true))
|
||||
return;
|
||||
|
||||
// all interactions should only happen when in range / unobstructed, so no range check is needed
|
||||
var interactUsingEvent = new InteractUsingEvent(user, used, target, clickLocation);
|
||||
RaiseLocalEvent(target.Uid, interactUsingEvent);
|
||||
@@ -696,6 +711,9 @@ namespace Content.Server.Interaction
|
||||
/// </summary>
|
||||
public async Task<bool> InteractUsingRanged(IEntity user, IEntity used, IEntity? target, EntityCoordinates clickLocation, bool inRangeUnobstructed)
|
||||
{
|
||||
if (await InteractDoBefore(user, used, inRangeUnobstructed ? target : null, clickLocation, false))
|
||||
return true;
|
||||
|
||||
if (target != null)
|
||||
{
|
||||
var rangedMsg = new RangedInteractEvent(user, used, target, clickLocation);
|
||||
@@ -715,10 +733,7 @@ namespace Content.Server.Interaction
|
||||
}
|
||||
}
|
||||
|
||||
if (inRangeUnobstructed)
|
||||
return await InteractDoAfter(user, used, target, clickLocation, false);
|
||||
else
|
||||
return await InteractDoAfter(user, used, null, clickLocation, false);
|
||||
return await InteractDoAfter(user, used, inRangeUnobstructed ? target : null, clickLocation, false);
|
||||
}
|
||||
|
||||
public void DoAttack(IEntity user, EntityCoordinates coordinates, bool wideAttack, EntityUid targetUid = default)
|
||||
|
||||
50
Content.Shared/Hands/Components/HandVirtualPullComponent.cs
Normal file
50
Content.Shared/Hands/Components/HandVirtualPullComponent.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Hands.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
[NetworkedComponent]
|
||||
public sealed class HandVirtualPullComponent : Component
|
||||
{
|
||||
private EntityUid _pulledEntity;
|
||||
public override string Name => "HandVirtualPull";
|
||||
|
||||
public EntityUid PulledEntity
|
||||
{
|
||||
get => _pulledEntity;
|
||||
set
|
||||
{
|
||||
_pulledEntity = value;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
return new VirtualPullComponentState(_pulledEntity);
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
if (curState is not VirtualPullComponentState pullState)
|
||||
return;
|
||||
|
||||
_pulledEntity = pullState.PulledEntity;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class VirtualPullComponentState : ComponentState
|
||||
{
|
||||
public readonly EntityUid PulledEntity;
|
||||
|
||||
public VirtualPullComponentState(EntityUid pulledEntity)
|
||||
{
|
||||
PulledEntity = pulledEntity;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,11 +55,11 @@ namespace Content.Shared.Hands.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string? _activeHand;
|
||||
|
||||
[ViewVariables]
|
||||
public IReadOnlyList<IReadOnlyHand> ReadOnlyHands => Hands;
|
||||
protected readonly List<Hand> Hands = new();
|
||||
public readonly List<Hand> Hands = new();
|
||||
|
||||
/// <summary>
|
||||
/// The amount of throw impulse per distance the player is from the throw target.
|
||||
@@ -89,7 +89,10 @@ namespace Content.Shared.Hands.Components
|
||||
|
||||
public virtual void HandsModified()
|
||||
{
|
||||
// todo axe all this for ECS.
|
||||
Dirty();
|
||||
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new HandsModifiedMessage { Hands = this });
|
||||
}
|
||||
|
||||
public void AddHand(string handName, HandLocation handLocation)
|
||||
@@ -100,7 +103,7 @@ namespace Content.Shared.Hands.Components
|
||||
var container = ContainerHelpers.CreateContainer<ContainerSlot>(Owner, handName);
|
||||
container.OccludesLight = false;
|
||||
|
||||
Hands.Add(new Hand(handName, true, handLocation, container));
|
||||
Hands.Add(new Hand(handName, handLocation, container));
|
||||
|
||||
if (ActiveHand == null)
|
||||
ActiveHand = handName;
|
||||
@@ -125,48 +128,55 @@ namespace Content.Shared.Hands.Components
|
||||
Hands.Remove(hand);
|
||||
|
||||
if (ActiveHand == hand.Name)
|
||||
ActiveHand = ReadOnlyHands.FirstOrDefault()?.Name;
|
||||
ActiveHand = Hands.FirstOrDefault()?.Name;
|
||||
|
||||
HandCountChanged();
|
||||
|
||||
HandsModified();
|
||||
}
|
||||
|
||||
public bool HasHand(string handName)
|
||||
{
|
||||
foreach (var hand in Hands)
|
||||
{
|
||||
if (hand.Name == handName)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Hand? GetHand(string handName)
|
||||
{
|
||||
foreach (var hand in Hands)
|
||||
{
|
||||
if (hand.Name == handName)
|
||||
return hand;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Hand? GetActiveHand()
|
||||
{
|
||||
if (ActiveHand == null)
|
||||
return null;
|
||||
|
||||
return GetHand(ActiveHand);
|
||||
return GetHandOrNull(ActiveHand);
|
||||
}
|
||||
|
||||
protected bool TryGetHand(string handName, [NotNullWhen(true)] out Hand? foundHand)
|
||||
public bool HasHand(string handName)
|
||||
{
|
||||
foundHand = GetHand(handName);
|
||||
return foundHand != null;
|
||||
return TryGetHand(handName, out _);
|
||||
}
|
||||
|
||||
protected bool TryGetActiveHand([NotNullWhen(true)] out Hand? activeHand)
|
||||
public Hand? GetHandOrNull(string handName)
|
||||
{
|
||||
return TryGetHand(handName, out var hand) ? hand : null;
|
||||
}
|
||||
|
||||
public Hand GetHand(string handName)
|
||||
{
|
||||
if (!TryGetHand(handName, out var hand))
|
||||
throw new KeyNotFoundException($"Unable to find hand with name {handName}");
|
||||
|
||||
return hand;
|
||||
}
|
||||
|
||||
public bool TryGetHand(string handName, [NotNullWhen(true)] out Hand? foundHand)
|
||||
{
|
||||
foreach (var hand in Hands)
|
||||
{
|
||||
if (hand.Name == handName)
|
||||
{
|
||||
foundHand = hand;
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
foundHand = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetActiveHand([NotNullWhen(true)] out Hand? activeHand)
|
||||
{
|
||||
activeHand = GetActiveHand();
|
||||
return activeHand != null;
|
||||
@@ -211,7 +221,7 @@ namespace Content.Shared.Hands.Components
|
||||
|
||||
public IEnumerable<IEntity> GetAllHeldEntities()
|
||||
{
|
||||
foreach (var hand in ReadOnlyHands)
|
||||
foreach (var hand in Hands)
|
||||
{
|
||||
if (hand.HeldEntity != null)
|
||||
yield return hand.HeldEntity;
|
||||
@@ -416,7 +426,7 @@ namespace Content.Shared.Hands.Components
|
||||
/// <summary>
|
||||
/// Drops a hands contents to the target location.
|
||||
/// </summary>
|
||||
private void DropHeldEntity(Hand hand, EntityCoordinates targetDropLocation, bool intentionalDrop = true)
|
||||
public void DropHeldEntity(Hand hand, EntityCoordinates targetDropLocation, bool intentionalDrop = true)
|
||||
{
|
||||
var heldEntity = hand.HeldEntity;
|
||||
|
||||
@@ -538,16 +548,7 @@ namespace Content.Shared.Hands.Components
|
||||
|
||||
public bool CanPickupEntityToActiveHand(IEntity entity, bool checkActionBlocker = true)
|
||||
{
|
||||
if (!TryGetActiveHand(out var hand))
|
||||
return false;
|
||||
|
||||
if (checkActionBlocker && !PlayerCanPickup())
|
||||
return false;
|
||||
|
||||
if (!CanInsertEntityIntoHand(hand, entity))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return ActiveHand != null && CanPickupEntity(ActiveHand, entity, checkActionBlocker);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -563,10 +564,7 @@ namespace Content.Shared.Hands.Components
|
||||
|
||||
public bool TryPickupEntityToActiveHand(IEntity entity, bool checkActionBlocker = true)
|
||||
{
|
||||
if (!TryGetActiveHand(out var hand))
|
||||
return false;
|
||||
|
||||
return TryPickupEntity(hand, entity, checkActionBlocker);
|
||||
return ActiveHand != null && TryPickupEntity(ActiveHand, entity, checkActionBlocker);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -574,9 +572,6 @@ namespace Content.Shared.Hands.Components
|
||||
/// </summary>
|
||||
protected bool CanInsertEntityIntoHand(Hand hand, IEntity entity)
|
||||
{
|
||||
if (!hand.Enabled)
|
||||
return false;
|
||||
|
||||
var handContainer = hand.Container;
|
||||
if (handContainer == null)
|
||||
return false;
|
||||
@@ -602,7 +597,7 @@ namespace Content.Shared.Hands.Components
|
||||
/// <summary>
|
||||
/// Puts an entity into the player's hand, assumes that the insertion is allowed.
|
||||
/// </summary>
|
||||
private void PutEntityIntoHand(Hand hand, IEntity entity)
|
||||
public void PutEntityIntoHand(Hand hand, IEntity entity)
|
||||
{
|
||||
var handContainer = hand.Container;
|
||||
if (handContainer == null)
|
||||
@@ -658,7 +653,7 @@ namespace Content.Shared.Hands.Components
|
||||
if (newActiveIndex > finalHandIndex)
|
||||
newActiveIndex = 0;
|
||||
|
||||
nextHand = ReadOnlyHands[newActiveIndex].Name;
|
||||
nextHand = Hands[newActiveIndex].Name;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -752,7 +747,7 @@ namespace Content.Shared.Hands.Components
|
||||
Hand? priorityHand = null;
|
||||
|
||||
if (priorityHandName != null)
|
||||
priorityHand = GetHand(priorityHandName);
|
||||
priorityHand = GetHandOrNull(priorityHandName);
|
||||
|
||||
return TryPutInAnyHand(entity, priorityHand, checkActionBlocker);
|
||||
}
|
||||
@@ -793,43 +788,16 @@ namespace Content.Shared.Hands.Components
|
||||
protected virtual void DoActivate(IEntity heldEntity) { }
|
||||
|
||||
protected virtual void HandlePickupAnimation(IEntity entity) { }
|
||||
|
||||
protected void EnableHand(Hand hand)
|
||||
{
|
||||
hand.Enabled = true;
|
||||
Dirty();
|
||||
}
|
||||
|
||||
protected void DisableHand(Hand hand)
|
||||
{
|
||||
hand.Enabled = false;
|
||||
DropHeldEntityToFloor(hand, intentionalDrop: false);
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public interface IReadOnlyHand
|
||||
public class Hand
|
||||
{
|
||||
[ViewVariables]
|
||||
public string Name { get; }
|
||||
|
||||
public bool Enabled { get; }
|
||||
|
||||
[ViewVariables]
|
||||
public HandLocation Location { get; }
|
||||
|
||||
public abstract IEntity? HeldEntity { get; }
|
||||
}
|
||||
|
||||
public class Hand : IReadOnlyHand
|
||||
{
|
||||
[ViewVariables]
|
||||
public string Name { get; set; }
|
||||
|
||||
[ViewVariables]
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
[ViewVariables]
|
||||
public HandLocation Location { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The container used to hold the contents of this hand. Nullable because the client must get the containers via <see cref="ContainerManagerComponent"/>,
|
||||
/// which may not be synced with the server when the client hands are created.
|
||||
@@ -840,37 +808,36 @@ namespace Content.Shared.Hands.Components
|
||||
[ViewVariables]
|
||||
public IEntity? HeldEntity => Container?.ContainedEntities?.FirstOrDefault();
|
||||
|
||||
public Hand(string name, bool enabled, HandLocation location, IContainer? container = null)
|
||||
public bool IsEmpty => HeldEntity == null;
|
||||
|
||||
public Hand(string name, HandLocation location, IContainer? container = null)
|
||||
{
|
||||
Name = name;
|
||||
Enabled = enabled;
|
||||
Location = location;
|
||||
Container = container;
|
||||
}
|
||||
|
||||
public HandState ToHandState()
|
||||
{
|
||||
return new(Name, Location, Enabled);
|
||||
return new(Name, Location);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class HandState
|
||||
public struct HandState
|
||||
{
|
||||
public string Name { get; }
|
||||
public HandLocation Location { get; }
|
||||
public bool Enabled { get; }
|
||||
|
||||
public HandState(string name, HandLocation location, bool enabled)
|
||||
public HandState(string name, HandLocation location)
|
||||
{
|
||||
Name = name;
|
||||
Location = location;
|
||||
Enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class HandsComponentState : ComponentState
|
||||
public sealed class HandsComponentState : ComponentState
|
||||
{
|
||||
public HandState[] Hands { get; }
|
||||
public string? ActiveHand { get; }
|
||||
@@ -886,25 +853,20 @@ namespace Content.Shared.Hands.Components
|
||||
/// A message that calls the use interaction on an item in hand, presumed for now the interaction will occur only on the active hand.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public class UseInHandMsg : ComponentMessage
|
||||
public sealed class UseInHandMsg : EntityEventArgs
|
||||
{
|
||||
public UseInHandMsg()
|
||||
{
|
||||
Directed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A message that calls the activate interaction on the item in the specified hand.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public class ActivateInHandMsg : ComponentMessage
|
||||
public class ActivateInHandMsg : EntityEventArgs
|
||||
{
|
||||
public string HandName { get; }
|
||||
|
||||
public ActivateInHandMsg(string handName)
|
||||
{
|
||||
Directed = true;
|
||||
HandName = handName;
|
||||
}
|
||||
}
|
||||
@@ -913,13 +875,12 @@ namespace Content.Shared.Hands.Components
|
||||
/// Uses the item in the active hand on the item in the specified hand.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public class ClientAttackByInHandMsg : ComponentMessage
|
||||
public class ClientInteractUsingInHandMsg : EntityEventArgs
|
||||
{
|
||||
public string HandName { get; }
|
||||
|
||||
public ClientAttackByInHandMsg(string handName)
|
||||
public ClientInteractUsingInHandMsg(string handName)
|
||||
{
|
||||
Directed = true;
|
||||
HandName = handName;
|
||||
}
|
||||
}
|
||||
@@ -928,28 +889,12 @@ namespace Content.Shared.Hands.Components
|
||||
/// Moves an item from one hand to the active hand.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public class MoveItemFromHandMsg : ComponentMessage
|
||||
public class MoveItemFromHandMsg : EntityEventArgs
|
||||
{
|
||||
public string HandName { get; }
|
||||
|
||||
public MoveItemFromHandMsg(string handName)
|
||||
{
|
||||
Directed = true;
|
||||
HandName = handName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the player's active hand to a specified hand.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public class ClientChangedHandMsg : ComponentMessage
|
||||
{
|
||||
public string HandName { get; }
|
||||
|
||||
public ClientChangedHandMsg(string handName)
|
||||
{
|
||||
Directed = true;
|
||||
HandName = handName;
|
||||
}
|
||||
}
|
||||
@@ -975,7 +920,7 @@ namespace Content.Shared.Hands.Components
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class PickupAnimationMessage : ComponentMessage
|
||||
public class PickupAnimationMessage : EntityEventArgs
|
||||
{
|
||||
public EntityUid EntityUid { get; }
|
||||
public EntityCoordinates InitialPosition { get; }
|
||||
@@ -983,10 +928,15 @@ namespace Content.Shared.Hands.Components
|
||||
|
||||
public PickupAnimationMessage(EntityUid entityUid, Vector2 pickupDirection, EntityCoordinates initialPosition)
|
||||
{
|
||||
Directed = true;
|
||||
EntityUid = entityUid;
|
||||
PickupDirection = pickupDirection;
|
||||
InitialPosition = initialPosition;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public struct HandsModifiedMessage
|
||||
{
|
||||
public SharedHandsComponent Hands;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Content.Shared.Hands.Components;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization;
|
||||
using System;
|
||||
|
||||
@@ -16,11 +15,7 @@ namespace Content.Shared.Hands
|
||||
SubscribeLocalEvent<SharedHandsComponent, EntRemovedFromContainerMessage>(HandleContainerModified);
|
||||
SubscribeLocalEvent<SharedHandsComponent, EntInsertedIntoContainerMessage>(HandleContainerModified);
|
||||
|
||||
SubscribeLocalEvent<RequestSetHandEvent>(HandleSetHand);
|
||||
SubscribeNetworkEvent<RequestSetHandEvent>(HandleSetHand);
|
||||
|
||||
SubscribeLocalEvent<RequestDropHeldEntityEvent>(HandleDrop);
|
||||
SubscribeNetworkEvent<RequestDropHeldEntityEvent>(HandleDrop);
|
||||
SubscribeAllEvent<RequestSetHandEvent>(HandleSetHand);
|
||||
}
|
||||
|
||||
public void DropHandItems(IEntity entity, bool doMobChecks = true)
|
||||
@@ -38,14 +33,16 @@ namespace Content.Shared.Hands
|
||||
|
||||
eventBus.RaiseLocalEvent(uid, msg);
|
||||
|
||||
if (msg.Cancelled) return;
|
||||
if (msg.Cancelled)
|
||||
return;
|
||||
|
||||
if (entity.TryGetContainerMan(out var containerManager))
|
||||
{
|
||||
var parentMsg = new ContainedEntityDropHandItemsAttemptEvent(uid);
|
||||
eventBus.RaiseLocalEvent(containerManager.Owner.Uid, parentMsg);
|
||||
|
||||
if (parentMsg.Cancelled) return;
|
||||
if (parentMsg.Cancelled)
|
||||
return;
|
||||
}
|
||||
|
||||
DropAllItemsInHands(entity, doMobChecks);
|
||||
@@ -55,9 +52,9 @@ namespace Content.Shared.Hands
|
||||
{
|
||||
}
|
||||
|
||||
private void HandleSetHand(RequestSetHandEvent msg, EntitySessionEventArgs eventArgs)
|
||||
private static void HandleSetHand(RequestSetHandEvent msg, EntitySessionEventArgs eventArgs)
|
||||
{
|
||||
var entity = eventArgs.SenderSession?.AttachedEntity;
|
||||
var entity = eventArgs.SenderSession.AttachedEntity;
|
||||
|
||||
if (entity == null || !entity.TryGetComponent(out SharedHandsComponent? hands))
|
||||
return;
|
||||
@@ -65,17 +62,13 @@ namespace Content.Shared.Hands
|
||||
hands.ActiveHand = msg.HandName;
|
||||
}
|
||||
|
||||
private void HandleDrop(RequestDropHeldEntityEvent msg, EntitySessionEventArgs eventArgs)
|
||||
protected virtual void HandleContainerModified(
|
||||
EntityUid uid,
|
||||
SharedHandsComponent component,
|
||||
ContainerModifiedMessage args)
|
||||
{
|
||||
var entity = eventArgs.SenderSession?.AttachedEntity;
|
||||
|
||||
if (entity == null || !entity.TryGetComponent(out SharedHandsComponent? hands))
|
||||
return;
|
||||
|
||||
hands.TryDropHand(msg.HandName, msg.DropTarget);
|
||||
component.Dirty();
|
||||
}
|
||||
|
||||
protected abstract void HandleContainerModified(EntityUid uid, SharedHandsComponent component, ContainerModifiedMessage args);
|
||||
}
|
||||
|
||||
public sealed class ContainedEntityDropHandItemsAttemptEvent : CancellableEntityEventArgs
|
||||
@@ -103,21 +96,4 @@ namespace Content.Shared.Hands
|
||||
HandName = handName;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class RequestDropHeldEntityEvent : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The hand to drop from.
|
||||
/// </summary>
|
||||
public string HandName { get; }
|
||||
|
||||
public EntityCoordinates DropTarget { get; }
|
||||
|
||||
public RequestDropHeldEntityEvent(string handName, EntityCoordinates dropTarget)
|
||||
{
|
||||
HandName = handName;
|
||||
DropTarget = dropTarget;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
53
Content.Shared/Interaction/BeforeInteract.cs
Normal file
53
Content.Shared/Interaction/BeforeInteract.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Shared.Interaction
|
||||
{
|
||||
/// <summary>
|
||||
/// Raised directed on the used object when clicking on another object before an interaction is handled.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public class BeforeInteractEvent : HandledEntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Entity that triggered the interaction.
|
||||
/// </summary>
|
||||
public IEntity User { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Entity that the user used to interact.
|
||||
/// </summary>
|
||||
public IEntity Used { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Entity that was interacted on. This can be null if the attack did not click on an entity.
|
||||
/// </summary>
|
||||
public IEntity? Target { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Location that the user clicked outside of their interaction range.
|
||||
/// </summary>
|
||||
public EntityCoordinates ClickLocation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the click location close enough to reach by the player? This does not check for obstructions, just that the target is within
|
||||
/// reach radius around the user.
|
||||
/// </summary>
|
||||
public bool CanReach { get; }
|
||||
|
||||
public BeforeInteractEvent(
|
||||
IEntity user,
|
||||
IEntity used,
|
||||
IEntity? target,
|
||||
EntityCoordinates clickLocation,
|
||||
bool canReach)
|
||||
{
|
||||
User = user;
|
||||
Used = used;
|
||||
Target = target;
|
||||
ClickLocation = clickLocation;
|
||||
CanReach = canReach;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ using JetBrains.Annotations;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Shared.DragDrop
|
||||
namespace Content.Shared.Interaction
|
||||
{
|
||||
/// <summary>
|
||||
/// This interface gives components behavior when they're dropped by a mob.
|
||||
@@ -50,6 +50,8 @@ namespace Content.Shared.Pulling.Components
|
||||
return;
|
||||
}
|
||||
|
||||
var eventBus = Owner.EntityManager.EventBus;
|
||||
|
||||
// New value. Abandon being pulled by any existing object.
|
||||
if (_puller != null)
|
||||
{
|
||||
@@ -64,10 +66,9 @@ namespace Content.Shared.Pulling.Components
|
||||
{
|
||||
var message = new PullStoppedMessage(oldPullerPhysics, _physics);
|
||||
|
||||
oldPuller.SendMessage(null, message);
|
||||
Owner.SendMessage(null, message);
|
||||
eventBus.RaiseLocalEvent(oldPuller.Uid, message, broadcast: false);
|
||||
eventBus.RaiseLocalEvent(Owner.Uid, message);
|
||||
|
||||
oldPuller.EntityManager.EventBus.RaiseEvent(EventSource.Local, message);
|
||||
_physics.WakeBody();
|
||||
}
|
||||
// else-branch warning is handled below
|
||||
@@ -125,14 +126,14 @@ namespace Content.Shared.Pulling.Components
|
||||
|
||||
var pullAttempt = new PullAttemptMessage(pullerPhysics, _physics);
|
||||
|
||||
value.SendMessage(null, pullAttempt);
|
||||
eventBus.RaiseLocalEvent(value.Uid, pullAttempt, broadcast: false);
|
||||
|
||||
if (pullAttempt.Cancelled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Owner.SendMessage(null, pullAttempt);
|
||||
eventBus.RaiseLocalEvent(Owner.Uid, pullAttempt);
|
||||
|
||||
if (pullAttempt.Cancelled)
|
||||
{
|
||||
@@ -147,10 +148,8 @@ namespace Content.Shared.Pulling.Components
|
||||
|
||||
var message = new PullStartedMessage(PullerPhysics, _physics);
|
||||
|
||||
_puller.SendMessage(null, message);
|
||||
Owner.SendMessage(null, message);
|
||||
|
||||
_puller.EntityManager.EventBus.RaiseEvent(EventSource.Local, message);
|
||||
eventBus.RaiseLocalEvent(_puller.Uid, message, broadcast: false);
|
||||
eventBus.RaiseLocalEvent(Owner.Uid, message);
|
||||
|
||||
var union = PullerPhysics.GetWorldAABB().Union(_physics.GetWorldAABB());
|
||||
var length = Math.Max(union.Size.X, union.Size.Y) * 0.75f;
|
||||
@@ -335,29 +334,6 @@ namespace Content.Shared.Pulling.Components
|
||||
Puller = entity;
|
||||
}
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
||||
{
|
||||
base.HandleMessage(message, component);
|
||||
|
||||
if (message is not PullMessage pullMessage ||
|
||||
pullMessage.Pulled.Owner != Owner)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var pulledStatus = Owner.GetComponentOrNull<SharedAlertsComponent>();
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case PullStartedMessage:
|
||||
pulledStatus?.ShowAlert(AlertType.Pulled);
|
||||
break;
|
||||
case PullStoppedMessage:
|
||||
pulledStatus?.ClearAlert(AlertType.Pulled);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnRemove()
|
||||
{
|
||||
TryStopPull();
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Physics.Pull;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Component = Robust.Shared.GameObjects.Component;
|
||||
|
||||
@@ -46,30 +44,5 @@ namespace Content.Shared.Pulling.Components
|
||||
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
||||
{
|
||||
base.HandleMessage(message, component);
|
||||
|
||||
if (message is not PullMessage pullMessage ||
|
||||
pullMessage.Puller.Owner != Owner)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SharedAlertsComponent? ownerStatus = Owner.GetComponentOrNull<SharedAlertsComponent>();
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case PullStartedMessage msg:
|
||||
Pulling = msg.Pulled.Owner;
|
||||
ownerStatus?.ShowAlert(AlertType.Pulling);
|
||||
break;
|
||||
case PullStoppedMessage _:
|
||||
Pulling = null;
|
||||
ownerStatus?.ClearAlert(AlertType.Pulling);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ using Robust.Shared.Physics;
|
||||
|
||||
namespace Content.Shared.Physics.Pull
|
||||
{
|
||||
public class PullMessage : ComponentMessage
|
||||
public class PullMessage : EntityEventArgs
|
||||
{
|
||||
public readonly IPhysBody Puller;
|
||||
public readonly IPhysBody Pulled;
|
||||
44
Content.Shared/Pulling/Systems/SharedPullerSystem.cs
Normal file
44
Content.Shared/Pulling/Systems/SharedPullerSystem.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Physics.Pull;
|
||||
using Content.Shared.Pulling.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Shared.Pulling
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class SharedPullerSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SharedPullerComponent, PullStartedMessage>(PullerHandlePullStarted);
|
||||
SubscribeLocalEvent<SharedPullerComponent, PullStoppedMessage>(PullerHandlePullStopped);
|
||||
}
|
||||
|
||||
private static void PullerHandlePullStarted(
|
||||
EntityUid uid,
|
||||
SharedPullerComponent component,
|
||||
PullStartedMessage args)
|
||||
{
|
||||
if (args.Puller.Owner.Uid != uid)
|
||||
return;
|
||||
|
||||
if (component.Owner.TryGetComponent(out SharedAlertsComponent? alerts))
|
||||
alerts.ShowAlert(AlertType.Pulling);
|
||||
}
|
||||
|
||||
private static void PullerHandlePullStopped(
|
||||
EntityUid uid,
|
||||
SharedPullerComponent component,
|
||||
PullStoppedMessage args)
|
||||
{
|
||||
if (args.Puller.Owner.Uid != uid)
|
||||
return;
|
||||
|
||||
if (component.Owner.TryGetComponent(out SharedAlertsComponent? alerts))
|
||||
alerts.ClearAlert(AlertType.Pulling);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Input;
|
||||
using Content.Shared.Physics.Pull;
|
||||
@@ -56,12 +57,34 @@ namespace Content.Shared.Pulling
|
||||
SubscribeLocalEvent<MoveEvent>(PullerMoved);
|
||||
SubscribeLocalEvent<EntInsertedIntoContainerMessage>(HandleContainerInsert);
|
||||
|
||||
SubscribeLocalEvent<SharedPullableComponent, PullStartedMessage>(PullableHandlePullStarted);
|
||||
SubscribeLocalEvent<SharedPullableComponent, PullStoppedMessage>(PullableHandlePullStopped);
|
||||
|
||||
CommandBinds.Builder
|
||||
.Bind(ContentKeyFunctions.MovePulledObject, new PointerInputCmdHandler(HandleMovePulledObject))
|
||||
.Bind(ContentKeyFunctions.ReleasePulledObject, InputCmdHandler.FromDelegate(HandleReleasePulledObject))
|
||||
.Register<SharedPullingSystem>();
|
||||
}
|
||||
|
||||
// Raise a "you are being pulled" alert if the pulled entity has alerts.
|
||||
private static void PullableHandlePullStarted(EntityUid uid, SharedPullableComponent component, PullStartedMessage args)
|
||||
{
|
||||
if (args.Pulled.Owner.Uid != uid)
|
||||
return;
|
||||
|
||||
if (component.Owner.TryGetComponent(out SharedAlertsComponent? alerts))
|
||||
alerts.ShowAlert(AlertType.Pulled);
|
||||
}
|
||||
|
||||
private static void PullableHandlePullStopped(EntityUid uid, SharedPullableComponent component, PullStoppedMessage args)
|
||||
{
|
||||
if (args.Pulled.Owner.Uid != uid)
|
||||
return;
|
||||
|
||||
if (component.Owner.TryGetComponent(out SharedAlertsComponent? alerts))
|
||||
alerts.ClearAlert(AlertType.Pulled);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
@@ -0,0 +1,8 @@
|
||||
# This item is stored in the hand slot while you are pulling something, to represent that and what you are pulling.
|
||||
- type: entity
|
||||
id: HandVirtualPull
|
||||
name: VIRTUAL PULL YOU SHOULD NOT SEE THIS
|
||||
abstract: true
|
||||
components:
|
||||
- type: Item
|
||||
- type: HandVirtualPull
|
||||
Reference in New Issue
Block a user