Files
FORT-BO/Assets/CONTENT/Game/Scripts/Interaction/NetworkHeldInteractable.cs
T

159 lines
4.6 KiB
C#

using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
public abstract class NetworkHeldInteractable : NetworkInteractable
{
protected const ulong NoHolder = ulong.MaxValue;
[Header("Physics")]
[SerializeField] private Rigidbody physicsBody;
[SerializeField] private Collider[] objectColliders;
[SerializeField, Min(0.05f)] private float collisionRefreshInterval = 0.25f;
private readonly NetworkVariable<ulong> holderClientId = new(
NoHolder,
NetworkVariableReadPermission.Everyone,
NetworkVariableWritePermission.Server);
private readonly HashSet<Collider> ignoredPlayerColliders = new();
private float nextCollisionRefreshTime;
protected abstract Vector3 HoldingOffset { get; }
protected abstract string DropPrompt { get; }
private bool IsHeld => holderClientId.Value != NoHolder;
private bool IsHeldByLocalPlayer =>
NetworkManager != null && holderClientId.Value == NetworkManager.LocalClientId;
public override string InteractionPrompt => !IsHeld
? InteractionLabel
: IsHeldByLocalPlayer
? DropPrompt
: "In use";
public override bool IsInteractionAvailable => base.IsInteractionAvailable &&
(!IsHeld || IsHeldByLocalPlayer);
private void Awake()
{
if (physicsBody == null)
physicsBody = GetComponent<Rigidbody>();
if (objectColliders == null || objectColliders.Length == 0)
objectColliders = GetComponentsInChildren<Collider>(true);
}
public override void OnNetworkSpawn()
{
holderClientId.OnValueChanged += HandleHolderChanged;
ApplyHoldingState(IsHeld);
}
public override void OnNetworkDespawn()
{
holderClientId.OnValueChanged -= HandleHolderChanged;
ApplyHoldingState(false);
}
private void LateUpdate()
{
if (!IsSpawned || !IsHeld)
return;
if (!TryGetPlayerObject(holderClientId.Value, out NetworkObject holder))
{
if (IsServer)
StopHolding();
return;
}
transform.SetPositionAndRotation(
holder.transform.TransformPoint(HoldingOffset),
holder.transform.rotation);
if (Time.time >= nextCollisionRefreshTime)
{
nextCollisionRefreshTime = Time.time + collisionRefreshInterval;
IgnoreCollisionsWithPlayers();
}
}
protected override bool CanInteractOnServer(ulong senderClientId) =>
!IsHeld || holderClientId.Value == senderClientId;
protected override void InteractOnServer(ulong senderClientId, NetworkObject playerObject)
{
if (holderClientId.Value == senderClientId)
StopHolding();
else if (!IsHeld)
StartHolding(senderClientId);
}
private void StartHolding(ulong clientId)
{
holderClientId.Value = clientId;
ApplyHoldingState(true);
}
private void StopHolding()
{
holderClientId.Value = NoHolder;
ApplyHoldingState(false);
}
private void HandleHolderChanged(ulong _, ulong currentHolder) =>
ApplyHoldingState(currentHolder != NoHolder);
private void ApplyHoldingState(bool isHeld)
{
if (physicsBody != null)
{
physicsBody.isKinematic = isHeld;
if (!isHeld)
physicsBody.linearVelocity = Vector3.zero;
}
if (isHeld)
IgnoreCollisionsWithPlayers();
else
RestorePlayerCollisions();
}
private void IgnoreCollisionsWithPlayers()
{
foreach (CharacterController playerCollider in FindObjectsByType<CharacterController>(
FindObjectsInactive.Exclude,
FindObjectsSortMode.None))
{
foreach (Collider objectCollider in objectColliders)
{
if (objectCollider == null || playerCollider == null)
continue;
Physics.IgnoreCollision(objectCollider, playerCollider, true);
}
ignoredPlayerColliders.Add(playerCollider);
}
}
private void RestorePlayerCollisions()
{
foreach (Collider playerCollider in ignoredPlayerColliders)
{
if (playerCollider == null)
continue;
foreach (Collider objectCollider in objectColliders)
{
if (objectCollider != null)
Physics.IgnoreCollision(objectCollider, playerCollider, false);
}
}
ignoredPlayerColliders.Clear();
}
}