using System; using Unity.Netcode; using UnityEngine; public enum RocketAssemblyQuality : byte { Normal, Defective } public struct RocketCriticalImpactState : INetworkSerializable, IEquatable { public uint Sequence; public Vector3 Point; public float Speed; public RocketCriticalImpactState(uint sequence, Vector3 point, float speed) { Sequence = sequence; Point = point; Speed = speed; } public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter { serializer.SerializeValue(ref Sequence); serializer.SerializeValue(ref Point); serializer.SerializeValue(ref Speed); } public bool Equals(RocketCriticalImpactState other) => Sequence == other.Sequence && Point == other.Point && Speed.Equals(other.Speed); } [DisallowMultipleComponent] [RequireComponent(typeof(NetworkItem), typeof(Rigidbody))] public sealed class NetworkRocketItem : NetworkBehaviour { [SerializeField] private NetworkItem networkItem; [SerializeField] private Rigidbody physicsBody; private readonly NetworkVariable assemblyQuality = new( RocketAssemblyQuality.Normal, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Server); private readonly NetworkVariable criticalImpactState = new( default, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Server); public RocketItemDefinition Definition => networkItem != null && networkItem.TryGetDefinition(out RocketItemDefinition definition) ? definition : null; public string RocketTypeId => Definition != null ? Definition.RocketTypeId : string.Empty; public RocketAssemblyQuality AssemblyQuality => assemblyQuality.Value; public bool IsDefective => AssemblyQuality == RocketAssemblyQuality.Defective; public bool IsCriticallyDamaged => criticalImpactState.Value.Sequence > 0; public uint CriticalImpactSequence => criticalImpactState.Value.Sequence; public Vector3 LastCriticalImpactPoint => criticalImpactState.Value.Point; public float LastCriticalImpactSpeed => criticalImpactState.Value.Speed; public event Action Changed; public event Action CriticalImpact; private void Reset() => ResolveReferences(); private void Awake() => ResolveReferences(); public override void OnNetworkSpawn() { assemblyQuality.OnValueChanged += HandleAssemblyQualityChanged; criticalImpactState.OnValueChanged += HandleCriticalImpactChanged; if (Definition == null) { Debug.LogError( $"{name} has NetworkRocketItem but its NetworkItem does not reference " + "a RocketItemDefinition.", this); } Changed?.Invoke(); } public override void OnNetworkDespawn() { assemblyQuality.OnValueChanged -= HandleAssemblyQualityChanged; criticalImpactState.OnValueChanged -= HandleCriticalImpactChanged; } private void OnCollisionEnter(Collision collision) { if (!IsServer || !IsSpawned || IsCriticallyDamaged || Definition == null) return; float impactSpeed = collision.relativeVelocity.magnitude; if (impactSpeed < Definition.CriticalImpactSpeed) return; Vector3 impactPoint = collision.contactCount > 0 ? collision.GetContact(0).point : transform.position; TryTriggerCriticalImpact(impactPoint, impactSpeed); } public bool TryTriggerCriticalImpact(Vector3 impactPoint, float impactSpeed) { if (!IsServer || !IsSpawned || IsCriticallyDamaged || Definition == null || impactSpeed < Definition.CriticalImpactSpeed) { return false; } criticalImpactState.Value = new RocketCriticalImpactState( criticalImpactState.Value.Sequence + 1, impactPoint, impactSpeed); return true; } public bool TryInitializeAssemblyQuality(bool isDefectiveResult) { if (!IsServer || !IsSpawned) return false; assemblyQuality.Value = isDefectiveResult ? RocketAssemblyQuality.Defective : RocketAssemblyQuality.Normal; return true; } [ContextMenu("Debug/Trigger Critical Impact (Server Play Mode)")] private void TriggerCriticalImpactFromContextMenu() { if (Application.isPlaying && IsServer && IsSpawned && Definition != null) { TryTriggerCriticalImpact( transform.position, Definition.CriticalImpactSpeed); } } private void ResolveReferences() { if (networkItem == null) networkItem = GetComponent(); if (physicsBody == null) physicsBody = GetComponent(); } private void HandleAssemblyQualityChanged( RocketAssemblyQuality _, RocketAssemblyQuality __) => Changed?.Invoke(); private void HandleCriticalImpactChanged( RocketCriticalImpactState previous, RocketCriticalImpactState current) { Changed?.Invoke(); if (current.Sequence != previous.Sequence) CriticalImpact?.Invoke(); } }