Files
FORT-BO/Assets/CONTENT/Game/Scripts/Items/NetworkHandItemInteractable.cs
T
2026-08-06 18:56:32 +03:00

184 lines
5.2 KiB
C#

using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
public interface IUsableHandItem
{
string ItemName { get; }
bool IsHeld { get; }
bool IsHeldByLocalPlayer { get; }
void RequestUse();
void RequestThrow();
}
public abstract class NetworkHandItemInteractable : NetworkInteractable, IUsableHandItem
{
private const ulong NoHolder = ulong.MaxValue;
private static readonly HashSet<NetworkHandItemInteractable> SpawnedItems = new();
[Header("Hand item physics")]
[SerializeField] private Rigidbody physicsBody;
[SerializeField] private Collider[] itemColliders;
[SerializeField, Min(0f)] private float throwForce = 4f;
[SerializeField, Min(0f)] private float throwUpwardForce = 1f;
private readonly NetworkVariable<ulong> holderClientId = new(
NoHolder,
NetworkVariableReadPermission.Everyone,
NetworkVariableWritePermission.Server);
public string ItemName => InteractionLabel;
public bool IsHeld => holderClientId.Value != NoHolder;
public bool IsHeldByLocalPlayer =>
NetworkManager != null && holderClientId.Value == NetworkManager.LocalClientId;
public override bool IsInteractionAvailable => base.IsInteractionAvailable && !IsHeld;
protected virtual void Awake()
{
if (physicsBody == null)
physicsBody = GetComponent<Rigidbody>();
if (itemColliders == null || itemColliders.Length == 0)
itemColliders = GetComponentsInChildren<Collider>(true);
}
public override void OnNetworkSpawn()
{
SpawnedItems.Add(this);
holderClientId.OnValueChanged += HandleHolderChanged;
ApplyHeldState(IsHeld);
}
public override void OnNetworkDespawn()
{
holderClientId.OnValueChanged -= HandleHolderChanged;
SpawnedItems.Remove(this);
ApplyHeldState(false);
}
private void LateUpdate()
{
if (!IsSpawned || !IsHeld)
return;
if (!TryGetPlayerObject(holderClientId.Value, out NetworkObject holder))
{
if (IsServer)
DropWithoutThrow();
return;
}
PlayerHandController hand = holder.GetComponent<PlayerHandController>();
if (hand == null || hand.ItemSocket == null)
return;
transform.SetPositionAndRotation(
hand.ItemSocket.position,
hand.ItemSocket.rotation);
}
public void RequestUse()
{
if (IsSpawned && IsHeldByLocalPlayer)
RequestUseServerRpc();
}
public void RequestThrow()
{
if (IsSpawned && IsHeldByLocalPlayer)
RequestThrowServerRpc();
}
protected override bool CanInteractOnServer(ulong senderClientId) =>
!IsHeld && !TryGetHeldItem(senderClientId, out _);
protected override void InteractOnServer(ulong senderClientId, NetworkObject playerObject)
{
holderClientId.Value = senderClientId;
ApplyHeldState(true);
}
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
private void RequestUseServerRpc(RpcParams rpcParams = default)
{
ulong senderClientId = rpcParams.Receive.SenderClientId;
if (holderClientId.Value != senderClientId ||
!TryGetPlayerObject(senderClientId, out NetworkObject playerObject))
{
return;
}
UseOnServer(playerObject);
}
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
private void RequestThrowServerRpc(RpcParams rpcParams = default)
{
ulong senderClientId = rpcParams.Receive.SenderClientId;
if (holderClientId.Value != senderClientId ||
!TryGetPlayerObject(senderClientId, out NetworkObject playerObject))
{
return;
}
Vector3 throwVelocity = playerObject.transform.forward * throwForce +
Vector3.up * throwUpwardForce;
holderClientId.Value = NoHolder;
ApplyHeldState(false);
if (physicsBody != null)
physicsBody.linearVelocity = throwVelocity;
}
protected abstract void UseOnServer(NetworkObject playerObject);
public static bool TryGetHeldItem(
ulong clientId,
out NetworkHandItemInteractable heldItem)
{
foreach (NetworkHandItemInteractable item in SpawnedItems)
{
if (item != null && item.holderClientId.Value == clientId)
{
heldItem = item;
return true;
}
}
heldItem = null;
return false;
}
private void DropWithoutThrow()
{
holderClientId.Value = NoHolder;
ApplyHeldState(false);
}
private void HandleHolderChanged(ulong _, ulong currentHolder) =>
ApplyHeldState(currentHolder != NoHolder);
private void ApplyHeldState(bool held)
{
if (physicsBody != null)
{
physicsBody.isKinematic = held || !IsServer;
if (held)
{
physicsBody.linearVelocity = Vector3.zero;
physicsBody.angularVelocity = Vector3.zero;
}
}
foreach (Collider itemCollider in itemColliders)
{
if (itemCollider != null)
itemCollider.enabled = !held;
}
}
}