* Initial framework for pulling. * Make it possible to pull items via (temporary) keybind Ctrl+Click, make items follow the player correctly. * Make other objects pullable, implement functionality for moving an object being pulled, make only one object able to be pulled at a time. * Make sure that MoveTo won't allow collisions with the player * Update everything to work with the new physics engine * Update Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs Co-authored-by: ComicIronic <comicironic@gmail.com> * Physics update and convert to direct type casts * Add notnull checks * Add pull keybinds to the tutorial window * Move PullController to shared * Fix pulled items getting left behind * Fix moving pulled objects into walls * Remove flooring of coordinates when moving pulled objects * Add missing null check in PutInHand * Change pulling keybind to control and throwing to alt * Change PhysicsComponent references to IPhysicsComponent * Add trying to pull a pulled entity disabling the pull * Add pulled status effect * Fix merge conflicts * Merge fixes * Make players pullable * Fix being able to pull yourself * Change pull moving to use a velocity * Update pulled and pulling icons to not be buckle A tragedy * Make pulled and pulling icons more consistent * Remove empty not pulled and not pulling images * Pulled icon update * Pulled icon update * Add clicking pulling status effect to stop the pull * Fix spacewalking when pulling * Merge conflict fixes * Add a pull verb * Fix nullable error * Add pulling through the entity drop down menu Co-authored-by: Jackson Lewis <inquisitivepenguin@protonmail.com> Co-authored-by: ComicIronic <comicironic@gmail.com>
258 lines
7.0 KiB
C#
258 lines
7.0 KiB
C#
#nullable enable
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
using Content.Client.UserInterface;
|
|
using Content.Shared.GameObjects.Components.Items;
|
|
using Robust.Client.GameObjects;
|
|
using Robust.Client.Interfaces.GameObjects.Components;
|
|
using Robust.Shared.GameObjects;
|
|
using Robust.Shared.Interfaces.GameObjects;
|
|
using Robust.Shared.IoC;
|
|
using Robust.Shared.ViewVariables;
|
|
|
|
namespace Content.Client.GameObjects.Components.Items
|
|
{
|
|
[RegisterComponent]
|
|
[ComponentReference(typeof(ISharedHandsComponent))]
|
|
public class HandsComponent : SharedHandsComponent
|
|
{
|
|
private HandsGui? _gui;
|
|
|
|
#pragma warning disable 649
|
|
[Dependency] private readonly IGameHud _gameHud = default!;
|
|
#pragma warning restore 649
|
|
|
|
private readonly List<Hand> _hands = new List<Hand>();
|
|
|
|
[ViewVariables] public IReadOnlyList<Hand> Hands => _hands;
|
|
|
|
[ViewVariables] public string? ActiveIndex { get; private set; }
|
|
|
|
[ViewVariables] private ISpriteComponent? _sprite;
|
|
|
|
[ViewVariables] public IEntity? ActiveHand => GetEntity(ActiveIndex);
|
|
|
|
private void AddHand(Hand hand)
|
|
{
|
|
_hands.Insert(hand.Index, hand);
|
|
}
|
|
|
|
public Hand? GetHand(string? name)
|
|
{
|
|
return Hands.FirstOrDefault(hand => hand.Name == name);
|
|
}
|
|
|
|
private bool TryHand(string name, [MaybeNullWhen(false)] out Hand hand)
|
|
{
|
|
return (hand = GetHand(name)) != null;
|
|
}
|
|
|
|
public IEntity? GetEntity(string? handName)
|
|
{
|
|
if (handName == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return GetHand(handName)?.Entity;
|
|
}
|
|
|
|
public override void OnRemove()
|
|
{
|
|
base.OnRemove();
|
|
|
|
_gui?.Dispose();
|
|
}
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
if (Owner.TryGetComponent(out _sprite))
|
|
{
|
|
foreach (var hand in _hands)
|
|
{
|
|
_sprite.LayerMapReserveBlank($"hand-{hand.Name}");
|
|
UpdateHandSprites(hand);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
|
{
|
|
if (curState == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var cast = (HandsComponentState) curState;
|
|
foreach (var sharedHand in cast.Hands)
|
|
{
|
|
if (!TryHand(sharedHand.Name, out var hand))
|
|
{
|
|
hand = new Hand(sharedHand, Owner.EntityManager);
|
|
AddHand(hand);
|
|
}
|
|
else
|
|
{
|
|
hand.Location = sharedHand.Location;
|
|
|
|
hand.Entity = sharedHand.EntityUid.HasValue
|
|
? Owner.EntityManager.GetEntity(sharedHand.EntityUid.Value)
|
|
: null;
|
|
}
|
|
|
|
UpdateHandSprites(hand);
|
|
}
|
|
|
|
foreach (var currentHand in _hands.ToList())
|
|
{
|
|
if (cast.Hands.All(newHand => newHand.Name != currentHand.Name))
|
|
{
|
|
_hands.Remove(currentHand);
|
|
_gui?.RemoveHand(currentHand);
|
|
HideHand(currentHand);
|
|
}
|
|
}
|
|
|
|
ActiveIndex = cast.ActiveIndex;
|
|
|
|
_gui?.UpdateHandIcons();
|
|
RefreshInHands();
|
|
}
|
|
|
|
private void HideHand(Hand hand)
|
|
{
|
|
_sprite?.LayerSetVisible($"hand-{hand.Name}", false);
|
|
}
|
|
|
|
private void UpdateHandSprites(Hand hand)
|
|
{
|
|
if (_sprite == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var entity = hand.Entity;
|
|
var name = hand.Name;
|
|
|
|
if (entity == null)
|
|
{
|
|
_sprite.LayerSetVisible($"hand-{name}", false);
|
|
return;
|
|
}
|
|
|
|
if (!entity.TryGetComponent(out ItemComponent item)) return;
|
|
|
|
var maybeInHands = item.GetInHandStateInfo(name);
|
|
|
|
if (!maybeInHands.HasValue)
|
|
{
|
|
_sprite.LayerSetVisible($"hand-{name}", false);
|
|
}
|
|
else
|
|
{
|
|
var (rsi, state) = maybeInHands.Value;
|
|
_sprite.LayerSetVisible($"hand-{name}", true);
|
|
_sprite.LayerSetState($"hand-{name}", state, rsi);
|
|
}
|
|
}
|
|
|
|
public void RefreshInHands()
|
|
{
|
|
if (!Initialized) return;
|
|
|
|
foreach (var hand in _hands)
|
|
{
|
|
UpdateHandSprites(hand);
|
|
}
|
|
}
|
|
|
|
protected override void Startup()
|
|
{
|
|
ActiveIndex = _hands.LastOrDefault()?.Name;
|
|
}
|
|
|
|
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
|
{
|
|
base.HandleMessage(message, component);
|
|
|
|
switch (message)
|
|
{
|
|
case PlayerAttachedMsg _:
|
|
if (_gui == null)
|
|
{
|
|
_gui = new HandsGui();
|
|
}
|
|
else
|
|
{
|
|
_gui.Parent?.RemoveChild(_gui);
|
|
}
|
|
|
|
_gameHud.HandsContainer.AddChild(_gui);
|
|
_gui.UpdateHandIcons();
|
|
break;
|
|
|
|
case PlayerDetachedMsg _:
|
|
_gui?.Parent?.RemoveChild(_gui);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void SendChangeHand(string index)
|
|
{
|
|
SendNetworkMessage(new ClientChangedHandMsg(index));
|
|
}
|
|
|
|
public void AttackByInHand(string index)
|
|
{
|
|
SendNetworkMessage(new ClientAttackByInHandMsg(index));
|
|
}
|
|
|
|
public void UseActiveHand()
|
|
{
|
|
if (GetEntity(ActiveIndex) != null)
|
|
{
|
|
SendNetworkMessage(new UseInHandMsg());
|
|
}
|
|
}
|
|
|
|
public void ActivateItemInHand(string handIndex)
|
|
{
|
|
if (GetEntity(handIndex) == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SendNetworkMessage(new ActivateInHandMsg(handIndex));
|
|
}
|
|
}
|
|
|
|
public class Hand
|
|
{
|
|
// TODO: Separate into server hand and client hand
|
|
public Hand(SharedHand hand, IEntityManager manager, HandButton? button = null)
|
|
{
|
|
Index = hand.Index;
|
|
Name = hand.Name;
|
|
Location = hand.Location;
|
|
Button = button;
|
|
|
|
if (!hand.EntityUid.HasValue)
|
|
{
|
|
return;
|
|
}
|
|
|
|
manager.TryGetEntity(hand.EntityUid.Value, out var entity);
|
|
Entity = entity;
|
|
}
|
|
|
|
public int Index { get; }
|
|
public string Name { get; }
|
|
public HandLocation Location { get; set; }
|
|
public IEntity? Entity { get; set; }
|
|
public HandButton? Button { get; set; }
|
|
}
|
|
}
|