* HandsGuiState * Gui state setting methods * code cleanup * Removes TryGetHands * ClientHand * Gui Hands * Refactor WIP 1 * Hand index * refactors 2 * wip 3 * wip 4 * wiip 4 * wip 5 * wip 6 * wip 7 * wip 8 * wip 9 * wip 11 * Hand ui mostly looks fine * hands gui cleanup 1 * cleanup 2 * wip 13 * hand enabled * stuff * Hands gui gap fix * onpressed test * hand gui buttons events work * bag activation works * fix item use * todo comment * hands activate fix * Moves Client Hands back to using strings to identify active hand * fixes action hand highlighting * diff fix * serverhand * SharedHand * SharedHand, IReadOnlyHand * Client Hands only stores SharedHand * cleanup server hands * server hand container shutdown * misc renames, refactors of serverhand * stuff 1 * stuff 3 * server hand refactor 1 * Undo API changes to remove massive diff * More API name fixes * server hands cleanup 2 * cleanup 3 * dropping cleanup * Cleanup 4 * MoveItemFromHand * Stuff * region sorting * Hand Putter methods cleanup * stuff 2 * Merges all of serverhand and clienthand into sharedhand * Other hands systems, hack to make inhands update (gui state set every frame, visualzier updated every frame) * GetFinalDropCoordinates cleanup * SwapHands cleanup * Moves server hands code to shared hands * Fixed hand selected and deselected * Naming fixes * Server hands system cleanup * Hands privacy fixes * Client hand updates when containers are modified * HeldItemVisualizer * Fixes hand gui item status panel * method name fix * Swap hands prediction * Dropping prediction * Fixes pickup entity animation * Fixes HeldItemsVisualizer * moves item pickup to shared * PR cleanup * fixes hand enabling/disabling * build fix * Conflict fixes * Fixes pickup animation * Uses component directed message subscriptions * event unsubscriptions in hand system * unsubscribe fix * CanInsertEntityIntoHand checks if hand is enabled * Moving items from one hand to another checks if the hands can pick up and drop * Fixes stop pulling not re-enabling hand * Fixes pickup animation for entities containers on the floor * Fixes using held items * Fixes multiple hands guis appearing * test fix * removes obsolete system sunsubscribes * Checks IsFirstTimePredicted before playing drop animation * fixes hand item deleted crash * Uses Get to get other system * Replaces AppearanceComponent with SharedAppearanceComponent * Replaces EnsureComponent with TryGetComponent * Improves event class names * Moves property up to top of class * Moves code for determining the hand visualizer rsi state into the visualizer instead of being determined on hand component * Eventbus todo comment * Yaml fix for changed visualizer name * Makes HandsVisuals a byte * Removes state from HandsVisualizer * Fixes hand using interaction method name * Namespace changes fixes * Fix for changed hand interaction method * missing } * conflict build fix * Moves cleint HandsSystem to correct folder * Moved namespace fix for interaction test * Moves Handsvisualizer ot correct folder * Moves SharedHandsSystem to correct folder * Fixes errors from moving namespace of hand systems * Fixes PDA component changes * Fixes ActionsComponent diff * Fixes inventory component diff * fixes null ref * Replaces obsolete Loc.GetString usage with fluent translations * Fluent for hands disarming * SwapHands and Drop user input specify to the server which hand * Pickup animation WorldPosiiton todo * Cleans up hands gui subscription handling * Fixes change in ActionBlockerSystem access * Namespace references fixes * HandsComponent PlayerAttached/Detached messages are handled through eventbus * Fixes GasCanisterSystem drop method usage * Fix gameticker equipping method at new location Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
268 lines
9.5 KiB
C#
268 lines
9.5 KiB
C#
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.Shared.Actions.Components;
|
|
using Content.Shared.Actions.Prototypes;
|
|
using Robust.Client.GameObjects;
|
|
using Robust.Client.Player;
|
|
using Robust.Client.UserInterface;
|
|
using Robust.Shared.GameObjects;
|
|
using Robust.Shared.Input.Binding;
|
|
using Robust.Shared.IoC;
|
|
using Robust.Shared.Log;
|
|
using Robust.Shared.ViewVariables;
|
|
|
|
namespace Content.Client.Actions
|
|
{
|
|
/// <inheritdoc/>
|
|
[RegisterComponent]
|
|
[ComponentReference(typeof(SharedActionsComponent))]
|
|
public sealed class ClientActionsComponent : SharedActionsComponent
|
|
{
|
|
public const byte Hotbars = 9;
|
|
public const byte Slots = 10;
|
|
|
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
|
|
|
[ComponentDependency] private readonly HandsComponent? _handsComponent = null;
|
|
[ComponentDependency] private readonly ClientInventoryComponent? _inventoryComponent = null;
|
|
|
|
private ActionsUI? _ui;
|
|
private readonly List<ItemSlotButton> _highlightingItemSlots = new();
|
|
|
|
/// <summary>
|
|
/// Current assignments for all hotbars / slots for this entity.
|
|
/// </summary>
|
|
public ActionAssignments Assignments { get; } = new(Hotbars, Slots);
|
|
|
|
/// <summary>
|
|
/// Allows calculating if we need to act due to this component being controlled by the current mob
|
|
/// </summary>
|
|
[ViewVariables]
|
|
private bool CurrentlyControlled => _playerManager.LocalPlayer != null && _playerManager.LocalPlayer.ControlledEntity == Owner;
|
|
|
|
protected override void Shutdown()
|
|
{
|
|
base.Shutdown();
|
|
PlayerDetached();
|
|
}
|
|
|
|
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
|
{
|
|
base.HandleComponentState(curState, nextState);
|
|
|
|
if (curState is not ActionComponentState)
|
|
{
|
|
return;
|
|
}
|
|
|
|
UpdateUI();
|
|
}
|
|
|
|
public void PlayerAttached()
|
|
{
|
|
if (!CurrentlyControlled || _ui != null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_ui = new ActionsUI(this);
|
|
IoCManager.Resolve<IUserInterfaceManager>().StateRoot.AddChild(_ui);
|
|
UpdateUI();
|
|
}
|
|
|
|
public void PlayerDetached()
|
|
{
|
|
if (_ui == null) return;
|
|
IoCManager.Resolve<IUserInterfaceManager>().StateRoot.RemoveChild(_ui);
|
|
_ui = null;
|
|
}
|
|
|
|
public void HandleHotbarKeybind(byte slot, in PointerInputCmdHandler.PointerInputCmdArgs args)
|
|
{
|
|
_ui?.HandleHotbarKeybind(slot, args);
|
|
}
|
|
|
|
public void HandleChangeHotbarKeybind(byte hotbar, in PointerInputCmdHandler.PointerInputCmdArgs args)
|
|
{
|
|
_ui?.HandleChangeHotbarKeybind(hotbar, args);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the displayed hotbar (and menu) based on current state of actions.
|
|
/// </summary>
|
|
private void UpdateUI()
|
|
{
|
|
if (!CurrentlyControlled || _ui == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Assignments.Reconcile(_ui.SelectedHotbar, ActionStates(), ItemActionStates(), _ui.Locked);
|
|
|
|
_ui.UpdateUI();
|
|
}
|
|
|
|
public void AttemptAction(ActionSlot slot)
|
|
{
|
|
|
|
var attempt = slot.ActionAttempt();
|
|
if (attempt == null) return;
|
|
|
|
switch (attempt.Action.BehaviorType)
|
|
{
|
|
case BehaviorType.Instant:
|
|
// for instant actions, we immediately tell the server we're doing it
|
|
SendNetworkMessage(attempt.PerformInstantActionMessage());
|
|
break;
|
|
case BehaviorType.Toggle:
|
|
// for toggle actions, we immediately tell the server we're toggling it.
|
|
if (attempt.TryGetActionState(this, out var actionState))
|
|
{
|
|
// TODO: At the moment we always predict that the toggle will work clientside,
|
|
// even if it sometimes may not (it will be reset by the server if wrong).
|
|
attempt.ToggleAction(this, !actionState.ToggledOn);
|
|
slot.ToggledOn = !actionState.ToggledOn;
|
|
SendNetworkMessage(attempt.PerformToggleActionMessage(!actionState.ToggledOn));
|
|
}
|
|
else
|
|
{
|
|
Logger.ErrorS("action", "attempted to toggle action {0} which has" +
|
|
" unknown state", attempt);
|
|
}
|
|
|
|
break;
|
|
case BehaviorType.TargetPoint:
|
|
case BehaviorType.TargetEntity:
|
|
// for target actions, we go into "select target" mode, we don't
|
|
// message the server until we actually pick our target.
|
|
|
|
// if we're clicking the same thing we're already targeting for, then we simply cancel
|
|
// targeting
|
|
_ui?.ToggleTargeting(slot);
|
|
break;
|
|
case BehaviorType.None:
|
|
break;
|
|
default:
|
|
Logger.ErrorS("action", "unhandled action press for action {0}",
|
|
attempt);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles clicks when selecting the target for an action. Only has an effect when currently
|
|
/// selecting a target.
|
|
/// </summary>
|
|
public bool TargetingOnUse(in PointerInputCmdHandler.PointerInputCmdArgs args)
|
|
{
|
|
// not currently predicted
|
|
if (EntitySystem.Get<InputSystem>().Predicted) return false;
|
|
|
|
// only do something for actual target-based actions
|
|
if (_ui?.SelectingTargetFor?.Action == null ||
|
|
(!_ui.SelectingTargetFor.Action.IsTargetAction)) return false;
|
|
|
|
// do nothing if we know it's on cooldown
|
|
if (_ui.SelectingTargetFor.IsOnCooldown) return false;
|
|
|
|
var attempt = _ui.SelectingTargetFor.ActionAttempt();
|
|
if (attempt == null)
|
|
{
|
|
_ui.StopTargeting();
|
|
return false;
|
|
}
|
|
|
|
switch (_ui.SelectingTargetFor.Action.BehaviorType)
|
|
{
|
|
case BehaviorType.TargetPoint:
|
|
{
|
|
// send our action to the server, we chose our target
|
|
SendNetworkMessage(attempt.PerformTargetPointActionMessage(args));
|
|
if (!attempt.Action.Repeat)
|
|
{
|
|
_ui.StopTargeting();
|
|
}
|
|
return true;
|
|
}
|
|
// target the currently hovered entity, if there is one
|
|
case BehaviorType.TargetEntity when args.EntityUid != EntityUid.Invalid:
|
|
{
|
|
// send our action to the server, we chose our target
|
|
SendNetworkMessage(attempt.PerformTargetEntityActionMessage(args));
|
|
if (!attempt.Action.Repeat)
|
|
{
|
|
_ui.StopTargeting();
|
|
}
|
|
return true;
|
|
}
|
|
// we are supposed to target an entity but we didn't click it
|
|
case BehaviorType.TargetEntity when args.EntityUid == EntityUid.Invalid:
|
|
{
|
|
if (attempt.Action.DeselectWhenEntityNotClicked)
|
|
_ui.StopTargeting();
|
|
return false;
|
|
}
|
|
default:
|
|
_ui.StopTargeting();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
protected override void AfterActionChanged()
|
|
{
|
|
UpdateUI();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Highlights the item slot (inventory or hand) that contains this item
|
|
/// </summary>
|
|
/// <param name="item"></param>
|
|
public void HighlightItemSlot(IEntity item)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stops highlighting any item slots we are currently highlighting.
|
|
/// </summary>
|
|
public void StopHighlightingItemSlots()
|
|
{
|
|
foreach (var itemSlot in _highlightingItemSlots)
|
|
{
|
|
itemSlot.Highlight(false);
|
|
}
|
|
_highlightingItemSlots.Clear();
|
|
}
|
|
|
|
public void ToggleActionsMenu()
|
|
{
|
|
_ui?.ToggleActionsMenu();
|
|
}
|
|
}
|
|
}
|