Partial hand ECS (#5634)

Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com>
Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com>
Co-authored-by: Paul <ritter.paul1@googlemail.com>
This commit is contained in:
Leon Friedrich
2022-01-05 17:53:08 +13:00
committed by GitHub
parent 03ad20758e
commit adbc4ee5b0
34 changed files with 781 additions and 963 deletions

View File

@@ -30,6 +30,7 @@ namespace Content.Client.Animations
}
var sprite = entMan.GetComponent<SpriteComponent>(animatableClone);
sprite.CopyFrom(sprite0);
sprite.Visible = true;
var animations = entMan.GetComponent<AnimationPlayerComponent>(animatableClone);
animations.AnimationCompleted += (_) => {

View File

@@ -1,5 +1,5 @@
using Content.Shared.Hands.Components;
using Robust.Shared.Containers;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
@@ -7,48 +7,9 @@ namespace Content.Client.Hands
{
[RegisterComponent]
[ComponentReference(typeof(SharedHandsComponent))]
[Friend(typeof(HandsSystem))]
public class HandsComponent : SharedHandsComponent
{
public HandsGui? Gui { get; set; }
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
if (curState is not HandsComponentState state)
return;
Hands.Clear();
foreach (var handState in state.Hands)
{
var newHand = new Hand(handState.Name, handState.Location);
Hands.Add(newHand);
}
ActiveHand = state.ActiveHand;
HandsModified();
}
public override void HandsModified()
{
UpdateHandContainers();
base.HandsModified();
}
public void UpdateHandContainers()
{
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent<ContainerManagerComponent?>(Owner, out var containerMan))
return;
foreach (var hand in Hands)
{
if (hand.Container == null)
{
containerMan.TryGetContainer(hand.Name, out var container);
hand.Container = container;
}
}
}
}
}

View File

@@ -59,24 +59,18 @@ namespace Content.Client.Hands
{
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()
public void Update(HandsGuiState state)
{
var state = _handsSystem.GetGuiState();
ActiveHand = state.ActiveHand;
_hands = state.GuiHands;
Array.Sort(_hands, HandOrderComparer.Instance);
@@ -97,7 +91,7 @@ namespace Content.Client.Hands
newButton.OnPressed += args => OnHandPressed(args, handName);
newButton.OnStoragePressed += _ => OnStoragePressed(handName);
_itemSlotManager.SetItemSlot(newButton, hand.HeldItem ?? EntityUid.Invalid);
_itemSlotManager.SetItemSlot(newButton, hand.HeldItem);
// Show blocked overlay if hand is blocked.
newButton.Blocked.Visible =
@@ -107,7 +101,7 @@ namespace Content.Client.Hands
if (TryGetActiveHand(out var activeHand))
{
activeHand.HandButton.SetActiveHand(true);
StatusPanel.Update(activeHand.HeldItem ?? EntityUid.Invalid);
StatusPanel.Update(activeHand.HeldItem);
}
}
@@ -119,7 +113,7 @@ namespace Content.Client.Hands
}
else if (TryGetHand(handName, out var hand))
{
_itemSlotManager.OnButtonPressed(args, hand.HeldItem ?? EntityUid.Invalid);
_itemSlotManager.OnButtonPressed(args, hand.HeldItem);
}
}
@@ -156,7 +150,7 @@ namespace Content.Client.Hands
foreach (var hand in _hands)
{
_itemSlotManager.UpdateCooldown(hand.HandButton, hand.HeldItem ?? EntityUid.Invalid);
_itemSlotManager.UpdateCooldown(hand.HandButton, hand.HeldItem);
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Client.Animations;
using Content.Client.HUD;
@@ -9,8 +9,10 @@ using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Input.Binding;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Timing;
namespace Content.Client.Hands
@@ -22,8 +24,6 @@ namespace Content.Client.Hands
[Dependency] private readonly IGameHud _gameHud = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
public event Action? GuiStateUpdated;
public override void Initialize()
{
base.Initialize();
@@ -31,70 +31,107 @@ namespace Content.Client.Hands
SubscribeLocalEvent<HandsComponent, PlayerAttachedEvent>(HandlePlayerAttached);
SubscribeLocalEvent<HandsComponent, PlayerDetachedEvent>(HandlePlayerDetached);
SubscribeLocalEvent<HandsComponent, ComponentRemove>(HandleCompRemove);
SubscribeLocalEvent<HandsModifiedMessage>(HandleHandsModified);
SubscribeLocalEvent<HandsComponent, ComponentHandleState>(HandleComponentState);
SubscribeNetworkEvent<PickupAnimationMessage>(HandlePickupAnimation);
SubscribeNetworkEvent<PickupAnimationEvent>(HandlePickupAnimation);
}
public override void Shutdown()
#region StateHandling
private void HandleComponentState(EntityUid uid, HandsComponent component, ref ComponentHandleState args)
{
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)
GuiStateUpdated?.Invoke();
}
private void HandlePickupAnimation(PickupAnimationMessage msg)
{
if (!EntityManager.EntityExists(msg.EntityUid))
if (args.Current is not HandsComponentState state)
return;
// Do we have a NEW hand?
var handsModified = component.Hands.Count != state.Hands.Count;
if (!handsModified)
{
for (var i = 0; i < state.Hands.Count; i++)
{
if (component.Hands[i].Name != state.Hands[i].Name ||
component.Hands[i].Location != state.Hands[i].Location)
{
handsModified = true;
break;
}
}
}
if (handsModified)
{
// we have new hands, get the new containers.
component.Hands = state.Hands;
UpdateHandContainers(uid, component);
}
TrySetActiveHand(uid, state.ActiveHand, component);
}
/// <summary>
/// Used to update the hand-containers when hands have been added or removed. Also updates the GUI
/// </summary>
public void UpdateHandContainers(EntityUid uid, HandsComponent? hands = null, ContainerManagerComponent? containerMan = null)
{
if (!Resolve(uid, ref hands, ref containerMan))
return;
foreach (var hand in hands.Hands)
{
if (hand.Container == null)
{
hand.Container = hands.Owner.EnsureContainer<ContainerSlot>(hand.Name);
}
}
if (uid == _playerManager.LocalPlayer?.ControlledEntity)
UpdateGui();
}
#endregion
#region PickupAnimation
private void HandlePickupAnimation(PickupAnimationEvent msg)
{
PickupAnimation(msg.ItemUid, msg.InitialPosition, msg.FinalPosition);
}
public override void PickupAnimation(EntityUid item, EntityCoordinates initialPosition, Vector2 finalPosition,
EntityUid? exclude)
{
PickupAnimation(item, initialPosition, finalPosition);
}
public void PickupAnimation(EntityUid item, EntityCoordinates initialPosition, Vector2 finalPosition)
{
if (!_gameTiming.IsFirstTimePredicted)
return;
ReusableAnimations.AnimateEntityPickup(msg.EntityUid, msg.InitialPosition, msg.FinalPosition, EntityManager);
}
public HandsGuiState GetGuiState()
{
if (GetPlayerHandsComponent() is not { } 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);
if (finalPosition.EqualsApprox(initialPosition.Position, tolerance: 0.1f))
return;
ReusableAnimations.AnimateEntityPickup(item, initialPosition, finalPosition);
}
#endregion
public EntityUid? GetActiveHandEntity()
{
if (GetPlayerHandsComponent() is not { ActiveHand: { } active } hands)
return null;
return hands.GetHand(active).HeldEntity;
return TryGetPlayerHands(out var hands) && hands.TryGetActiveHeldEntity(out var entity)
? entity
: null;
}
private HandsComponent? GetPlayerHandsComponent()
/// <summary>
/// Get the hands component of the local player
/// </summary>
public bool TryGetPlayerHands([NotNullWhen(true)] out HandsComponent? hands)
{
var player = _playerManager.LocalPlayer?.ControlledEntity;
if (player is not {Valid: true} || !EntityManager.TryGetComponent(player.Value, out HandsComponent? hands))
return null;
return hands;
hands = null;
return player != null && TryComp(player.Value, out hands);
}
/// <summary>
/// Called when a user clicked on their hands GUI
/// </summary>
public void UIHandClick(HandsComponent hands, string handName)
{
if (!hands.TryGetHand(handName, out var pressedHand))
@@ -117,7 +154,7 @@ namespace Content.Client.Hands
if (pressedHand != activeHand && pressedEntity == null)
{
// change active hand
RaiseNetworkEvent(new RequestSetHandEvent(handName));
EntityManager.RaisePredictiveEvent(new RequestSetHandEvent(handName));
return;
}
@@ -135,9 +172,44 @@ namespace Content.Client.Hands
}
}
/// <summary>
/// Called when a user clicks on an item in their hands GUI.
/// </summary>
public void UIHandActivate(string handName)
{
RaiseNetworkEvent (new ActivateInHandMsg(handName));
RaiseNetworkEvent(new ActivateInHandMsg(handName));
}
#region Gui
public void UpdateGui(HandsComponent? hands = null)
{
if (hands == null && !TryGetPlayerHands(out hands) || hands.Gui == null)
return;
var states = hands.Hands
.Select(hand => new GuiHand(hand.Name, hand.Location, hand.HeldEntity))
.ToArray();
hands.Gui.Update(new HandsGuiState(states, hands.ActiveHand));
}
protected override void HandleContainerModified(EntityUid uid, SharedHandsComponent component, ContainerModifiedMessage args)
{
base.HandleContainerModified(uid, component, args);
if (uid == _playerManager.LocalPlayer?.ControlledEntity)
UpdateGui();
}
public override bool TrySetActiveHand(EntityUid uid, string? value, SharedHandsComponent? handComp = null)
{
if (!base.TrySetActiveHand(uid, value, handComp))
return false;
if (uid == _playerManager.LocalPlayer?.ControlledEntity)
UpdateGui();
return true;
}
private void HandlePlayerAttached(EntityUid uid, HandsComponent component, PlayerAttachedEvent args)
@@ -145,6 +217,7 @@ namespace Content.Client.Hands
component.Gui = new HandsGui(component, this);
_gameHud.HandsContainer.AddChild(component.Gui);
component.Gui.SetPositionFirst();
UpdateGui(component);
}
private static void HandlePlayerDetached(EntityUid uid, HandsComponent component, PlayerDetachedEvent args)
@@ -162,5 +235,6 @@ namespace Content.Client.Hands
comp.Gui?.Orphan();
comp.Gui = null;
}
#endregion
}
}

View File

@@ -1,6 +1,4 @@
using Content.Client.Hands;
using Content.Shared.Item;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
@@ -8,15 +6,5 @@ namespace Content.Client.Items.Components
{
[RegisterComponent]
[ComponentReference(typeof(SharedItemComponent))]
public class ItemComponent : SharedItemComponent
{
protected override void OnEquippedPrefixChange()
{
if (!Owner.TryGetContainer(out var container))
return;
if (IoCManager.Resolve<IEntityManager>().TryGetComponent(container.Owner, out HandsComponent? hands))
hands.UpdateHandVisualizer();
}
}
public class ItemComponent : SharedItemComponent { }
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using Content.Client.Items.UI;
using Robust.Client.UserInterface;
using Robust.Shared.GameObjects;
@@ -7,10 +7,10 @@ namespace Content.Client.Items.Managers
{
public interface IItemSlotManager
{
bool OnButtonPressed(GUIBoundKeyEventArgs args, EntityUid item);
void UpdateCooldown(ItemSlotButton? cooldownTexture, EntityUid entity);
bool OnButtonPressed(GUIBoundKeyEventArgs args, EntityUid? item);
void UpdateCooldown(ItemSlotButton? cooldownTexture, EntityUid? entity);
bool SetItemSlot(ItemSlotButton button, EntityUid? entity);
void HoverInSlot(ItemSlotButton button, EntityUid entity, bool fits);
void HoverInSlot(ItemSlotButton button, EntityUid? entity, bool fits);
event Action<EntitySlotHighlightedEventArgs>? EntityHighlightedUpdated;
bool IsHighlighted(EntityUid? uid);

View File

@@ -53,34 +53,34 @@ namespace Content.Client.Items.Managers
button.StorageButton.Visible = _entityManager.HasComponent<ClientStorageComponent>(entity);
}
button.Entity = entity;
button.Entity = entity ?? default;
// im lazy
button.UpdateSlotHighlighted();
return true;
}
public bool OnButtonPressed(GUIBoundKeyEventArgs args, EntityUid item)
public bool OnButtonPressed(GUIBoundKeyEventArgs args, EntityUid? item)
{
if (item == default)
if (item == null)
return false;
if (args.Function == ContentKeyFunctions.ExamineEntity)
{
_entitySystemManager.GetEntitySystem<ExamineSystem>()
.DoExamine(item);
.DoExamine(item.Value);
}
else if (args.Function == ContentKeyFunctions.OpenContextMenu)
{
_entitySystemManager.GetEntitySystem<VerbSystem>().VerbMenu.OpenVerbMenu(item);
_entitySystemManager.GetEntitySystem<VerbSystem>().VerbMenu.OpenVerbMenu(item.Value);
}
else if (args.Function == ContentKeyFunctions.ActivateItemInWorld)
{
_entityManager.EntityNetManager?.SendSystemNetworkMessage(new InteractInventorySlotEvent(item, altInteract: false));
_entityManager.EntityNetManager?.SendSystemNetworkMessage(new InteractInventorySlotEvent(item.Value, altInteract: false));
}
else if (args.Function == ContentKeyFunctions.AltActivateItemInWorld)
{
_entityManager.RaisePredictiveEvent(new InteractInventorySlotEvent(item, altInteract: true));
_entityManager.RaisePredictiveEvent(new InteractInventorySlotEvent(item.Value, altInteract: true));
}
else
{
@@ -90,7 +90,7 @@ namespace Content.Client.Items.Managers
return true;
}
public void UpdateCooldown(ItemSlotButton? button, EntityUid entity)
public void UpdateCooldown(ItemSlotButton? button, EntityUid? entity)
{
var cooldownDisplay = button?.CooldownDisplay;
@@ -99,7 +99,7 @@ namespace Content.Client.Items.Managers
return;
}
if (entity == default || _entityManager.Deleted(entity) ||
if (entity == null || _entityManager.Deleted(entity) ||
!_entityManager.TryGetComponent(entity, out ItemCooldownComponent? cooldown) ||
!cooldown.CooldownStart.HasValue ||
!cooldown.CooldownEnd.HasValue)
@@ -119,9 +119,9 @@ namespace Content.Client.Items.Managers
cooldownDisplay.Visible = ratio > -1f;
}
public void HoverInSlot(ItemSlotButton button, EntityUid entity, bool fits)
public void HoverInSlot(ItemSlotButton button, EntityUid? entity, bool fits)
{
if (entity == default || !button.MouseIsHovering)
if (entity == null || !button.MouseIsHovering)
{
button.ClearHover();
return;
@@ -135,7 +135,7 @@ namespace Content.Client.Items.Managers
// Set green / red overlay at 50% transparency
var hoverEntity = _entityManager.SpawnEntity("hoverentity", MapCoordinates.Nullspace);
var hoverSprite = _entityManager.GetComponent<SpriteComponent>(hoverEntity);
hoverSprite.CopyFrom(_entityManager.GetComponent<SpriteComponent>(entity));
hoverSprite.CopyFrom(_entityManager.GetComponent<SpriteComponent>(entity.Value));
hoverSprite.Color = fits ? new Color(0, 255, 0, 127) : new Color(255, 0, 0, 127);
button.HoverSpriteView.Sprite = hoverSprite;

View File

@@ -33,7 +33,7 @@ namespace Content.Client.Items.UI
private readonly PanelContainer _panel;
[ViewVariables]
private EntityUid _entity;
private EntityUid? _entity;
public ItemStatusPanel(Texture texture, StyleBox.Margin cutout, StyleBox.Margin flat, Label.AlignMode textAlign)
{
@@ -130,19 +130,19 @@ namespace Content.Client.Items.UI
UpdateItemName();
}
public void Update(EntityUid entity)
public void Update(EntityUid? entity)
{
if (entity == default)
if (entity == null)
{
ClearOldStatus();
_entity = default;
_entity = null;
_panel.Visible = false;
return;
}
if (entity != _entity)
{
_entity = entity;
_entity = entity.Value;
BuildNewEntityStatus();
UpdateItemName();
@@ -153,7 +153,7 @@ namespace Content.Client.Items.UI
private void UpdateItemName()
{
if (_entity == default)
if (_entity == null)
return;
if (_entityManager.TryGetComponent(_entity, out HandVirtualItemComponent? virtualItem)
@@ -163,7 +163,7 @@ namespace Content.Client.Items.UI
}
else
{
_itemNameLabel.Text = _entityManager.GetComponent<MetaDataComponent>(_entity).EntityName;
_itemNameLabel.Text = _entityManager.GetComponent<MetaDataComponent>(_entity.Value).EntityName;
}
}
@@ -185,7 +185,7 @@ namespace Content.Client.Items.UI
ClearOldStatus();
foreach (var statusComponent in _entityManager.GetComponents<IItemStatus>(_entity))
foreach (var statusComponent in _entityManager.GetComponents<IItemStatus>(_entity!.Value))
{
var control = statusComponent.MakeControl();
_statusContents.AddChild(control);
@@ -194,7 +194,7 @@ namespace Content.Client.Items.UI
}
var collectMsg = new ItemStatusCollectMessage();
_entityManager.EventBus.RaiseLocalEvent(_entity, collectMsg);
_entityManager.EventBus.RaiseLocalEvent(_entity!.Value, collectMsg);
foreach (var control in collectMsg.Controls)
{