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

@@ -27,43 +27,14 @@ namespace Content.Shared.Hands.Components
public sealed override string Name => "Hands";
public event Action? OnItemChanged; //TODO: Try to replace C# event
/// <summary>
/// The name of the currently active hand.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public string? ActiveHand
{
get => _activeHand;
set
{
if (value != null && !HasHand(value))
{
Logger.Warning($"{nameof(SharedHandsComponent)} on {Owner} tried to set its active hand to {value}, which was not a hand.");
return;
}
if (value == null && Hands.Count != 0)
{
Logger.Error($"{nameof(SharedHandsComponent)} on {Owner} tried to set its active hand to null, when it still had another hand.");
_activeHand = Hands[0].Name;
return;
}
if (value != ActiveHand)
{
DeselectActiveHeldEntity();
_activeHand = value;
SelectActiveHeldEntity();
HandsModified();
}
}
}
private string? _activeHand;
[ViewVariables]
public string? ActiveHand;
[ViewVariables]
public readonly List<Hand> Hands = new();
public List<Hand> Hands = new();
/// <summary>
/// The amount of throw impulse per distance the player is from the throw target.
@@ -79,66 +50,25 @@ namespace Content.Shared.Hands.Components
[ViewVariables(VVAccess.ReadWrite)]
public float ThrowRange { get; set; } = 8f;
public override ComponentState GetComponentState()
{
var hands = new HandState[Hands.Count];
for (var i = 0; i < Hands.Count; i++)
{
var hand = Hands[i].ToHandState();
hands[i] = hand;
}
return new HandsComponentState(hands, ActiveHand);
}
public virtual void HandsModified()
{
// todo axe all this for ECS.
// todo burn it all down.
UpdateHandVisualizer();
Dirty();
_entMan.EventBus.RaiseEvent(EventSource.Local, new HandsModifiedMessage { Hands = this });
}
public void UpdateHandVisualizer()
{
var entMan = _entMan;
if (!entMan.TryGetComponent(Owner, out AppearanceComponent? appearance))
return;
var hands = new List<HandVisualState>();
foreach (var hand in Hands)
{
if (hand.HeldEntity == null)
continue;
if (!entMan.TryGetComponent(hand.HeldEntity, out SharedItemComponent? item) || item.RsiPath == null)
continue;
var handState = new HandVisualState(item.RsiPath, item.EquippedPrefix, hand.Location, item.Color);
hands.Add(handState);
}
appearance.SetData(HandsVisuals.VisualState, new HandsVisualState(hands));
}
private bool PlayerCanDrop => EntitySystem.Get<ActionBlockerSystem>().CanDrop(Owner);
private bool PlayerCanPickup => EntitySystem.Get<ActionBlockerSystem>().CanPickup(Owner);
public void AddHand(string handName, HandLocation handLocation)
{
if (HasHand(handName))
return;
var container = Owner.CreateContainer<ContainerSlot>(handName);
var container = Owner.EnsureContainer<ContainerSlot>(handName);
container.OccludesLight = false;
Hands.Add(new Hand(handName, handLocation, container));
ActiveHand ??= handName;
if (ActiveHand == null)
EntitySystem.Get<SharedHandsSystem>().TrySetActiveHand(Owner, handName, this);
HandCountChanged();
HandsModified();
Dirty();
}
public void RemoveHand(string handName)
@@ -156,14 +86,14 @@ namespace Content.Shared.Hands.Components
Hands.Remove(hand);
if (ActiveHand == hand.Name)
ActiveHand = Hands.FirstOrDefault()?.Name;
EntitySystem.Get<SharedHandsSystem>().TrySetActiveHand(Owner, Hands.FirstOrDefault()?.Name, this);
HandCountChanged();
HandsModified();
Dirty();
}
private Hand? GetActiveHand()
public Hand? GetActiveHand()
{
if (ActiveHand == null)
return null;
@@ -220,7 +150,7 @@ namespace Content.Shared.Hands.Components
return hand.HeldEntity != null;
}
public bool TryGetHeldEntity(string handName,[NotNullWhen(true)] out EntityUid? heldEntity)
public bool TryGetHeldEntity(string handName, [NotNullWhen(true)] out EntityUid? heldEntity)
{
heldEntity = null;
@@ -228,7 +158,7 @@ namespace Content.Shared.Hands.Components
return false;
heldEntity = hand.HeldEntity;
return hand.HeldEntity != null;
return heldEntity != null;
}
public bool TryGetActiveHeldEntity([NotNullWhen(true)] out EntityUid? heldEntity)
@@ -251,7 +181,7 @@ namespace Content.Shared.Hands.Components
{
foreach (var hand in Hands)
{
if (hand.HeldEntity.HasValue)
if (hand.HeldEntity != null)
yield return hand.HeldEntity.Value;
}
}
@@ -302,7 +232,7 @@ namespace Content.Shared.Hands.Components
if (!CanRemoveHeldEntityFromHand(hand))
return false;
if (checkActionBlocker && !PlayerCanDrop())
if (checkActionBlocker && !PlayerCanDrop)
return false;
return true;
@@ -406,7 +336,7 @@ namespace Content.Shared.Hands.Components
if (!CanRemoveHeldEntityFromHand(hand))
return false;
RemoveHeldEntityFromHand(hand);
EntitySystem.Get<SharedHandsSystem>().RemoveHeldEntityFromHand(Owner, hand, this);
return true;
}
@@ -418,62 +348,27 @@ namespace Content.Shared.Hands.Components
if (hand.HeldEntity == null)
return false;
return hand.Container?.CanRemove(hand.HeldEntity.Value) ?? false;
}
/// <summary>
/// Checks if the player is allowed to perform drops.
/// </summary>
private bool PlayerCanDrop()
{
if (!IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<ActionBlockerSystem>().CanDrop(Owner))
if (!hand.Container!.CanRemove(hand.HeldEntity.Value))
return false;
return true;
}
/// <summary>
/// Removes the contents of a hand from its container. Assumes that the removal is allowed.
/// </summary>
private void RemoveHeldEntityFromHand(Hand hand)
{
if (hand.HeldEntity is not { } heldEntity)
return;
var handContainer = hand.Container;
if (handContainer == null)
return;
if (hand.Name == ActiveHand)
DeselectActiveHeldEntity();
if (!handContainer.Remove(heldEntity))
{
Logger.Error($"{nameof(SharedHandsComponent)} on {Owner} could not remove {heldEntity} from {handContainer}.");
return;
}
OnHeldEntityRemovedFromHand(heldEntity, hand.ToHandState());
HandsModified();
}
/// <summary>
/// Drops a hands contents to the target location.
/// </summary>
public void DropHeldEntity(Hand hand, EntityCoordinates targetDropLocation)
{
if (hand.HeldEntity is not { } heldEntity)
if (hand.HeldEntity == null)
return;
RemoveHeldEntityFromHand(hand);
var heldEntity = hand.HeldEntity.Value;
EntitySystem.Get<SharedHandsSystem>().RemoveHeldEntityFromHand(Owner, hand, this);
EntitySystem.Get<SharedInteractionSystem>().DroppedInteraction(Owner, heldEntity);
_entMan.GetComponent<TransformComponent>(heldEntity).WorldPosition = GetFinalDropCoordinates(targetDropLocation);
OnItemChanged?.Invoke();
}
/// <summary>
@@ -508,7 +403,7 @@ namespace Content.Shared.Hands.Components
if (!CanRemoveHeldEntityFromHand(hand))
return false;
if (checkActionBlocker && !PlayerCanDrop())
if (checkActionBlocker && !PlayerCanDrop)
return false;
DropHeldEntity(hand, location);
@@ -525,10 +420,12 @@ namespace Content.Shared.Hands.Components
private bool CanPutHeldEntityIntoContainer(Hand hand, IContainer targetContainer, bool checkActionBlocker)
{
if (hand.HeldEntity is not { } heldEntity)
if (hand.HeldEntity == null)
return false;
if (checkActionBlocker && !PlayerCanDrop())
var heldEntity = hand.HeldEntity.Value;
if (checkActionBlocker && !PlayerCanDrop)
return false;
if (!targetContainer.CanInsert(heldEntity))
@@ -542,10 +439,12 @@ namespace Content.Shared.Hands.Components
/// </summary>
private void PutHeldEntityIntoContainer(Hand hand, IContainer targetContainer)
{
if (hand.HeldEntity is not { } heldEntity)
if (hand.HeldEntity == null)
return;
RemoveHeldEntityFromHand(hand);
var heldEntity = hand.HeldEntity.Value;
EntitySystem.Get<SharedHandsSystem>().RemoveHeldEntityFromHand(Owner, hand, this);
if (!targetContainer.Insert(heldEntity))
{
@@ -563,7 +462,7 @@ namespace Content.Shared.Hands.Components
if (!TryGetHand(handName, out var hand))
return false;
if (checkActionBlocker && !PlayerCanPickup())
if (checkActionBlocker && !PlayerCanPickup)
return false;
if (!CanInsertEntityIntoHand(hand, entity))
@@ -580,17 +479,17 @@ namespace Content.Shared.Hands.Components
/// <summary>
/// Tries to pick up an entity to a specific hand.
/// </summary>
public bool TryPickupEntity(string handName, EntityUid entity, bool checkActionBlocker = true)
public bool TryPickupEntity(string handName, EntityUid entity, bool checkActionBlocker = true, bool animateUser = false)
{
if (!TryGetHand(handName, out var hand))
return false;
return TryPickupEntity(hand, entity, checkActionBlocker);
return TryPickupEntity(hand, entity, checkActionBlocker, animateUser);
}
public bool TryPickupEntityToActiveHand(EntityUid entity, bool checkActionBlocker = true)
public bool TryPickupEntityToActiveHand(EntityUid entity, bool checkActionBlocker = true, bool animateUser = false)
{
return ActiveHand != null && TryPickupEntity(ActiveHand, entity, checkActionBlocker);
return ActiveHand != null && TryPickupEntity(ActiveHand, entity, checkActionBlocker, animateUser);
}
/// <summary>
@@ -598,6 +497,9 @@ namespace Content.Shared.Hands.Components
/// </summary>
protected bool CanInsertEntityIntoHand(Hand hand, EntityUid entity)
{
if (!_entMan.HasComponent<SharedItemComponent>(entity))
return false;
var handContainer = hand.Container;
if (handContainer == null) return false;
@@ -611,56 +513,23 @@ namespace Content.Shared.Hands.Components
return true;
}
/// <summary>
/// Checks if the player is allowed to perform pickup actions.
/// </summary>
/// <returns></returns>
protected bool PlayerCanPickup()
{
if (!EntitySystem.Get<ActionBlockerSystem>().CanPickup(Owner))
return false;
return true;
}
/// <summary>
/// Puts an entity into the player's hand, assumes that the insertion is allowed.
/// </summary>
public void PutEntityIntoHand(Hand hand, EntityUid entity)
{
var handContainer = hand.Container;
if (handContainer == null)
return;
if (!handContainer.Insert(entity))
{
Logger.Error($"{nameof(SharedHandsComponent)} on {Owner} could not insert {entity} into {handContainer}.");
return;
}
EntitySystem.Get<SharedInteractionSystem>().EquippedHandInteraction(Owner, entity, hand.ToHandState());
if (hand.Name == ActiveHand)
SelectActiveHeldEntity();
_entMan.GetComponent<TransformComponent>(entity).LocalPosition = Vector2.Zero;
OnItemChanged?.Invoke();
HandsModified();
}
private bool TryPickupEntity(Hand hand, EntityUid entity, bool checkActionBlocker = true)
private bool TryPickupEntity(Hand hand, EntityUid entity, bool checkActionBlocker = true, bool animateUser = false)
{
if (!CanInsertEntityIntoHand(hand, entity))
return false;
if (checkActionBlocker && !PlayerCanPickup())
if (checkActionBlocker && !PlayerCanPickup)
return false;
HandlePickupAnimation(entity);
PutEntityIntoHand(hand, entity);
EntitySystem.Get<SharedAdminLogSystem>().Add(LogType.Pickup, LogImpact.Low, $"{_entMan.ToPrettyString(Owner):user} picked up {_entMan.ToPrettyString(entity):entity}");
// animation
var handSys = EntitySystem.Get<SharedHandsSystem>();
var coordinateEntity = _entMan.GetComponent<TransformComponent>(Owner).Parent?.Owner ?? Owner;
var initialPosition = EntityCoordinates.FromMap(coordinateEntity, _entMan.GetComponent<TransformComponent>(entity).MapPosition);
var finalPosition = _entMan.GetComponent<TransformComponent>(Owner).LocalPosition;
handSys.PickupAnimation(entity, initialPosition, finalPosition, animateUser ? null : Owner);
handSys.PutEntityIntoHand(Owner, hand, entity, this);
return true;
}
@@ -740,28 +609,16 @@ namespace Content.Shared.Hands.Components
if (!CanInsertEntityIntoHand(activeHand, heldEntity.Value) || !CanRemoveHeldEntityFromHand(hand))
return false;
if (checkActionBlocker && (!PlayerCanDrop() || !PlayerCanPickup()))
if (checkActionBlocker && (!PlayerCanDrop || !PlayerCanPickup))
return false;
RemoveHeldEntityFromHand(hand);
PutEntityIntoHand(activeHand, heldEntity.Value);
EntitySystem.Get<SharedHandsSystem>().RemoveHeldEntityFromHand(Owner, hand, this);
EntitySystem.Get<SharedHandsSystem>().PutEntityIntoHand(Owner, activeHand, heldEntity.Value, this);
return true;
}
#endregion
private void DeselectActiveHeldEntity()
{
if (TryGetActiveHeldEntity(out var entity))
EntitySystem.Get<SharedInteractionSystem>().HandDeselectedInteraction(Owner, entity.Value);
}
private void SelectActiveHeldEntity()
{
if (TryGetActiveHeldEntity(out var entity))
EntitySystem.Get<SharedInteractionSystem>().HandSelectedInteraction(Owner, entity.Value);
}
private void HandCountChanged()
{
_entMan.EventBus.RaiseEvent(EventSource.Local, new HandCountChangedEvent(Owner));
@@ -772,36 +629,30 @@ namespace Content.Shared.Hands.Components
/// </summary>
public bool PutInHand(SharedItemComponent item, bool checkActionBlocker = true)
{
return TryPutInActiveHandOrAny(item.Owner, checkActionBlocker);
}
/// <summary>
/// Tries to pick up an entity into the active hand. If it cannot, tries to pick up the entity into each other hand.
/// </summary>
public bool PutInHand(EntityUid uid, bool checkActionBlocker = true)
{
return TryPutInActiveHandOrAny(uid, checkActionBlocker);
return PutInHand(item.Owner, checkActionBlocker);
}
/// <summary>
/// Puts an item any hand, prefering the active hand, or puts it on the floor under the player.
/// </summary>
public void PutInHandOrDrop(SharedItemComponent item, bool checkActionBlocker = true) =>
PutInHandOrDrop(item.Owner, checkActionBlocker);
public void PutInHandOrDrop(EntityUid uid, bool checkActionBlocker = true)
public void PutInHandOrDrop(EntityUid entity, bool checkActionBlocker = true)
{
if (!TryPutInActiveHandOrAny(uid, checkActionBlocker))
_entMan.GetComponent<TransformComponent>(uid).Coordinates = _entMan.GetComponent<TransformComponent>(Owner).Coordinates;
if (!PutInHand(entity, checkActionBlocker))
_entMan.GetComponent<TransformComponent>(entity).Coordinates = _entMan.GetComponent<TransformComponent>(Owner).Coordinates;
}
public void PutInHandOrDrop(SharedItemComponent item, bool checkActionBlocker = true)
{
PutInHandOrDrop(item.Owner, checkActionBlocker);
}
/// <summary>
/// Tries to pick up an entity into the active hand. If it cannot, tries to pick up the entity into each other hand.
/// </summary>
public bool TryPutInActiveHandOrAny(EntityUid entity, bool checkActionBlocker = true)
public bool PutInHand(EntityUid entity, bool checkActionBlocker = true)
{
return TryPutInAnyHand(entity, GetActiveHand(), checkActionBlocker);
return PutInHand(entity, GetActiveHand(), checkActionBlocker);
}
/// <summary>
@@ -814,13 +665,13 @@ namespace Content.Shared.Hands.Components
if (priorityHandName != null)
priorityHand = GetHandOrNull(priorityHandName);
return TryPutInAnyHand(entity, priorityHand, checkActionBlocker);
return PutInHand(entity, priorityHand, checkActionBlocker);
}
/// <summary>
/// Tries to pick up an entity into the priority hand, if provided. If it cannot, tries to pick up the entity into each other hand.
/// </summary>
private bool TryPutInAnyHand(EntityUid entity, Hand? priorityHand = null, bool checkActionBlocker = true)
private bool PutInHand(EntityUid entity, Hand? priorityHand = null, bool checkActionBlocker = true)
{
if (priorityHand != null)
{
@@ -836,9 +687,23 @@ namespace Content.Shared.Hands.Components
return false;
}
protected virtual void OnHeldEntityRemovedFromHand(EntityUid heldEntity, HandState handState) { }
/// <summary>
/// Checks if any hand can pick up an item.
/// </summary>
public bool CanPutInHand(SharedItemComponent item, bool mobCheck = true)
{
var entity = item.Owner;
protected virtual void HandlePickupAnimation(EntityUid entity) { }
if (mobCheck && !PlayerCanPickup)
return false;
foreach (var hand in Hands)
{
if (CanInsertEntityIntoHand(hand, entity))
return true;
}
return false;
}
}
#region visualizerData
@@ -877,6 +742,7 @@ namespace Content.Shared.Hands.Components
}
#endregion
[Serializable, NetSerializable]
public class Hand
{
[ViewVariables]
@@ -889,46 +755,29 @@ namespace Content.Shared.Hands.Components
/// 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.
/// </summary>
[ViewVariables]
public IContainer? Container { get; set; }
[ViewVariables, NonSerialized]
public ContainerSlot? Container;
[ViewVariables]
public EntityUid? HeldEntity => Container?.ContainedEntities?.Count > 0 ? Container.ContainedEntities[0] : null;
public EntityUid? HeldEntity => Container?.ContainedEntity;
public bool IsEmpty => HeldEntity == null;
public Hand(string name, HandLocation location, IContainer? container = null)
public Hand(string name, HandLocation location, ContainerSlot? container = null)
{
Name = name;
Location = location;
Container = container;
}
public HandState ToHandState()
{
return new(Name, Location);
}
}
[Serializable, NetSerializable]
public struct HandState
{
public string Name { get; }
public HandLocation Location { get; }
public HandState(string name, HandLocation location)
{
Name = name;
Location = location;
}
}
[Serializable, NetSerializable]
public sealed class HandsComponentState : ComponentState
{
public HandState[] Hands { get; }
public List<Hand> Hands { get; }
public string? ActiveHand { get; }
public HandsComponentState(HandState[] hands, string? activeHand = null)
public HandsComponentState(List<Hand> hands, string? activeHand = null)
{
Hands = hands;
ActiveHand = activeHand;
@@ -1004,25 +853,4 @@ namespace Content.Shared.Hands.Components
public EntityUid Sender { get; }
}
[Serializable, NetSerializable]
public class PickupAnimationMessage : EntityEventArgs
{
public EntityUid EntityUid { get; }
public EntityCoordinates InitialPosition { get; }
public Vector2 FinalPosition { get; }
public PickupAnimationMessage(EntityUid entityUid, Vector2 finalPosition, EntityCoordinates initialPosition)
{
EntityUid = entityUid;
FinalPosition = finalPosition;
InitialPosition = initialPosition;
}
}
[Serializable, NetSerializable]
public struct HandsModifiedMessage
{
public SharedHandsComponent Hands;
}
}