2025-09-24 11:24:38 +05:00

334 lines
6.4 KiB
C#

using System;
using Fusion;
using UnityEngine;
namespace TPSBR
{
public sealed class ItemBox : StaticNetworkTransform, IInteraction
{
// HELPERS
public enum EState
{
None,
Closed,
Open,
Locked,
}
// PRIVATE MEMBERS
[Header("Item Box")]
[SerializeField]
private float _autoCloseTime;
[SerializeField]
private float _unlockTime;
[SerializeField]
private EState _startState;
[SerializeField]
private Transform _lockedState;
[SerializeField]
private Transform _unlockedState;
[SerializeField]
private AnimationClip _openAnimation;
[SerializeField]
private AnimationClip _closeAnimation;
[Header("Interaction")]
[SerializeField]
private string _interactionName;
[SerializeField]
private string _interactionDescription;
[SerializeField]
private Transform _hudPivot;
[SerializeField]
private Collider _interactionCollider;
[Header("Pickups")]
[SerializeField]
private EBehaviour _behaviour;
[SerializeField]
private PickupPoint[] _pickupsSetup;
[Header("Audio")]
[SerializeField]
private AudioEffect _audioEffect;
[SerializeField]
private AudioSetup _openSound;
[SerializeField]
private AudioSetup _closeSound;
[Networked]
private EState BoxState { get; set; }
[Networked]
private int BoxTimer { get; set; }
private Animation _animation;
private StaticPickup[] _nestedPickups;
private EState _localState;
// PUBLIC METHODS
public void Open()
{
if (HasStateAuthority == false)
return;
if (BoxState != EState.Closed)
return;
BoxTimer = GetExpirationTick(_autoCloseTime);
BoxState = EState.Open;
if (_behaviour == EBehaviour.RandomOnOpen || _nestedPickups[0] == null)
{
for (int i = 0; i < _nestedPickups.Length; i++)
{
if (_nestedPickups[i] != null)
{
Runner.Despawn(_nestedPickups[i].Object);
}
_nestedPickups[i] = SpawnPickup(_pickupsSetup[i]);
}
}
else if (_behaviour == EBehaviour.RandomOnSpawn)
{
for (int i = 0; i < _nestedPickups.Length; i++)
{
_nestedPickups[i].Refresh();
}
}
UpdateLocalState();
}
// IInteraction INTERFACE
string IInteraction.Name => _interactionName;
string IInteraction.Description => _interactionDescription;
Vector3 IInteraction.HUDPosition => _hudPivot != null ? _hudPivot.position : transform.position;
bool IInteraction.IsActive => BoxState == EState.Closed;
// MonoBehaviour INTERFACE
private void Awake()
{
_animation = GetComponent<Animation>();
}
// NetworkBehaviour INTERFACE
public override void Spawned()
{
base.Spawned();
_localState = EState.None;
if (HasStateAuthority == false)
{
if (ApplicationSettings.IsStrippedBatch == true)
{
gameObject.SetActive(false);
}
UpdateLocalState();
return;
}
_nestedPickups = new StaticPickup[_pickupsSetup.Length];
switch (_startState)
{
case EState.None:
case EState.Closed:
Unlock();
break;
case EState.Open:
Open();
break;
case EState.Locked:
Lock();
break;
default:
break;
}
UpdateLocalState();
}
public override void FixedUpdateNetwork()
{
// Expired
if (BoxTimer <= 0 || BoxTimer > Runner.Tick)
return;
switch (BoxState)
{
case EState.Open: Lock(); break;
case EState.Locked: Unlock(); break;
}
UpdateLocalState();
}
public override void Render()
{
UpdateLocalState();
}
// PRIVATE METHODS
private void Lock()
{
if (BoxState == EState.Locked)
return;
for (int i = 0; i < _nestedPickups.Length; i++)
{
if (_nestedPickups[i] != null)
{
_nestedPickups[i].SetIsDisabled(true);
}
}
BoxTimer = GetExpirationTick(_unlockTime);
BoxState = EState.Locked;
}
private void Unlock()
{
BoxState = EState.Closed;
for (int i = 0; i < _nestedPickups.Length; i++)
{
if (_nestedPickups[i] != null)
{
_nestedPickups[i].SetIsDisabled(true);
}
}
}
private StaticPickup SpawnPickup(PickupPoint point)
{
var prefab = ChoosePickup(point.Pickups);
var pickup = Runner.Spawn(prefab, point.Transform.position, point.Transform.rotation);
pickup.SetBehaviour(StaticPickup.EBehaviour.Interaction, -1f);
pickup.PickupConsumed += OnPickupConsumed;
return pickup;
}
private void UpdateLocalState()
{
if (ApplicationSettings.IsStrippedBatch == true)
return;
if (_localState == BoxState)
return;
_localState = BoxState;
_lockedState.SetActive(_localState == EState.Locked);
_unlockedState.SetActive(_localState != EState.Locked);
_interactionCollider.enabled = _localState == EState.Closed;
switch (_localState)
{
case EState.Open:
if (_animation.clip != _openAnimation)
{
_animation.clip = _openAnimation;
_animation.Play();
_audioEffect.Play(_openSound, EForceBehaviour.ForceAny);
}
break;
case EState.Closed:
case EState.Locked:
if (_animation.clip != _closeAnimation)
{
_animation.clip = _closeAnimation;
_animation.Play();
_audioEffect.Play(_closeSound, EForceBehaviour.ForceAny);
}
break;
default:
break;
}
}
private StaticPickup ChoosePickup(PickupData[] data)
{
int totalProbability = 0;
for (int i = 0; i < data.Length; i++)
{
totalProbability += data[i].Probability;
}
if (totalProbability <= 0)
return null;
int targetProbability = UnityEngine.Random.Range(0, totalProbability);
int currentProbability = 0;
for (int i = 0; i < data.Length; i++)
{
currentProbability += data[i].Probability;
if (targetProbability < currentProbability)
{
return data[i].Pickup;
}
}
return null;
}
private void OnPickupConsumed(StaticPickup pickup)
{
if (pickup.AutoDespawn == false)
return;
int index = Array.IndexOf(_nestedPickups, pickup);
if (index >= 0)
{
// Clear reference, pickup will be auto despawned
_nestedPickups[index] = null;
}
}
private int GetExpirationTick(float time)
{
return Runner.Tick + Mathf.CeilToInt(time / Runner.DeltaTime);
}
// HELPERS
private enum EBehaviour
{
RandomOnSpawn,
RandomOnOpen,
}
[Serializable]
private class PickupPoint
{
public Transform Transform;
public PickupData[] Pickups;
}
[Serializable]
private class PickupData
{
public StaticPickup Pickup;
public int Probability = 1;
}
}
}