Added RocketAssemblyStation and ReadyRocket

This commit is contained in:
2026-08-18 16:12:00 +03:00
parent 7a0ee6b519
commit 63d2836dc9
85 changed files with 15087 additions and 3562 deletions
@@ -0,0 +1,168 @@
using System;
using Unity.Netcode;
using UnityEngine;
public enum RocketAssemblyQuality : byte
{
Normal,
Defective
}
public struct RocketCriticalImpactState : INetworkSerializable,
IEquatable<RocketCriticalImpactState>
{
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<T>(BufferSerializer<T> 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<RocketAssemblyQuality> assemblyQuality = new(
RocketAssemblyQuality.Normal,
NetworkVariableReadPermission.Everyone,
NetworkVariableWritePermission.Server);
private readonly NetworkVariable<RocketCriticalImpactState> 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<NetworkItem>();
if (physicsBody == null)
physicsBody = GetComponent<Rigidbody>();
}
private void HandleAssemblyQualityChanged(
RocketAssemblyQuality _,
RocketAssemblyQuality __) => Changed?.Invoke();
private void HandleCriticalImpactChanged(
RocketCriticalImpactState previous,
RocketCriticalImpactState current)
{
Changed?.Invoke();
if (current.Sequence != previous.Sequence)
CriticalImpact?.Invoke();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d1e6d4d81ab34d2e9ee3a1bfa201f111
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,102 @@
using UnityEngine;
using UnityEngine.Events;
[DisallowMultipleComponent]
public sealed class RocketItemView : MonoBehaviour
{
[SerializeField] private NetworkRocketItem rocket;
[Header("State")]
[SerializeField] private UnityEvent onNormalQualityApplied;
[SerializeField] private UnityEvent onDefectiveQualityApplied;
[SerializeField] private UnityEvent onDamagedStateApplied;
[Header("Critical Impact")]
[SerializeField] private UnityEvent onCriticalImpact;
[SerializeField] private UnityEvent<Vector3> onCriticalImpactPoint;
[SerializeField] private UnityEvent<float> onCriticalImpactSpeed;
private uint observedImpactSequence;
private RocketAssemblyQuality observedQuality;
private bool hasAppliedQuality;
private bool hasAppliedDamagedState;
private void Reset() => rocket = GetComponentInParent<NetworkRocketItem>();
private void Awake()
{
if (rocket == null)
rocket = GetComponentInParent<NetworkRocketItem>();
}
private void OnEnable()
{
if (rocket == null)
return;
observedImpactSequence = rocket.CriticalImpactSequence;
observedQuality = rocket.AssemblyQuality;
hasAppliedQuality = false;
hasAppliedDamagedState = false;
rocket.Changed += ApplyState;
rocket.CriticalImpact += HandleCriticalImpact;
ApplyState();
}
private void OnDisable()
{
if (rocket == null)
return;
rocket.Changed -= ApplyState;
rocket.CriticalImpact -= HandleCriticalImpact;
}
[ContextMenu("Preview/Critical Impact")]
private void PreviewCriticalImpact()
{
onCriticalImpactPoint?.Invoke(transform.position);
onCriticalImpactSpeed?.Invoke(7f);
onCriticalImpact?.Invoke();
onDamagedStateApplied?.Invoke();
}
[ContextMenu("Preview/Normal Quality")]
private void PreviewNormalQuality() => onNormalQualityApplied?.Invoke();
[ContextMenu("Preview/Defective Quality")]
private void PreviewDefectiveQuality() => onDefectiveQualityApplied?.Invoke();
private void ApplyState()
{
if (rocket == null)
return;
if (!hasAppliedQuality || observedQuality != rocket.AssemblyQuality)
{
observedQuality = rocket.AssemblyQuality;
hasAppliedQuality = true;
if (observedQuality == RocketAssemblyQuality.Defective)
onDefectiveQualityApplied?.Invoke();
else
onNormalQualityApplied?.Invoke();
}
if (rocket.IsCriticallyDamaged && !hasAppliedDamagedState)
{
hasAppliedDamagedState = true;
onDamagedStateApplied?.Invoke();
}
}
private void HandleCriticalImpact()
{
if (rocket == null || rocket.CriticalImpactSequence == observedImpactSequence)
return;
observedImpactSequence = rocket.CriticalImpactSequence;
onCriticalImpactPoint?.Invoke(rocket.LastCriticalImpactPoint);
onCriticalImpactSpeed?.Invoke(rocket.LastCriticalImpactSpeed);
onCriticalImpact?.Invoke();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d1e6d4d81ab34d2e9ee3a1bfa201f112
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: