using System.Collections; using NitroxClient.Communication; using NitroxClient.GameLogic.FMOD; using NitroxClient.GameLogic.Spawning.Metadata.Processor; using NitroxClient.MonoBehaviours; using NitroxClient.MonoBehaviours.CinematicController; using NitroxModel.DataStructures.GameLogic.Entities; using NitroxModel.DataStructures.GameLogic.Entities.Metadata; using NitroxModel.DataStructures.Util; using NitroxModel_Subnautica.DataStructures; using NitroxModel.DataStructures.GameLogic; using NitroxModel.Packets; using UnityEngine; namespace NitroxClient.GameLogic.Spawning.WorldEntities; public class EscapePodWorldEntitySpawner : IWorldEntitySpawner { /* * When creating additional escape pods (multiple users with multiple pods) * we want to suppress the escape pod's awake method so it doesn't override * EscapePod.main to the new escape pod. */ public static bool SuppressEscapePodAwakeMethod; private readonly LocalPlayer localPlayer; public EscapePodWorldEntitySpawner(LocalPlayer localPlayer) { this.localPlayer = localPlayer; } public IEnumerator SpawnAsync(WorldEntity entity, Optional parent, EntityCell cellRoot, TaskResult> result) { if (entity is not EscapePodWorldEntity escapePodEntity) { result.Set(Optional.Empty); Log.Error($"Received incorrect entity type: {entity.GetType()}"); yield break; } SuppressEscapePodAwakeMethod = true; GameObject escapePod = CreateNewEscapePod(escapePodEntity); SuppressEscapePodAwakeMethod = false; result.Set(Optional.Of(escapePod)); } private GameObject CreateNewEscapePod(EscapePodWorldEntity escapePodEntity) { // TODO: When we want to implement multiple escape pods, instantiate the prefab. Backlog task: #1945 // This will require some work as instantiating the prefab as-is will not make it visible. //GameObject escapePod = Object.Instantiate(EscapePod.main.gameObject); GameObject escapePod = EscapePod.main.gameObject; EscapePod pod = escapePod.GetComponent(); Object.DestroyImmediate(escapePod.GetComponent()); // if template has a pre-existing NitroxEntity, remove. NitroxEntity.SetNewId(escapePod, escapePodEntity.Id); if (escapePod.TryGetComponent(out Rigidbody rigidbody)) { rigidbody.constraints = RigidbodyConstraints.FreezeAll; } else { Log.Error("Escape pod did not have a rigid body!"); } pod.anchorPosition = escapePod.transform.position = escapePodEntity.Transform.Position.ToUnity(); pod.ForceSkyApplier(); pod.escapePodCinematicControl.StopAll(); // Player is not new and has completed the intro cinematic. If not EscapePod repair status is handled by the intro cinematic. if (escapePodEntity.Metadata is EscapePodMetadata metadata && localPlayer.IntroCinematicMode == IntroCinematicMode.COMPLETED) { using FMODSoundSuppressor soundSuppressor = FMODSystem.SuppressSubnauticaSounds(); using PacketSuppressor packetSuppressor = PacketSuppressor.Suppress(); Radio radio = pod.radioSpawner.spawnedObj.GetComponent(); EscapePodMetadataProcessor.ProcessInitialSyncMetadata(pod, radio, metadata); // NB: Entities.SpawnBatchAsync (which is the function calling the current spawner) // will still apply the metadata another time but we don't care as it's not destructive } FixStartMethods(escapePod); return escapePod; } /// /// Start() isn't executed for the EscapePod and children (Why? Idk, maybe because it's a scene...) so we call the components here where we have patches in Start. /// private static void FixStartMethods(GameObject escapePod) { foreach (FMOD_CustomEmitter customEmitter in escapePod.GetComponentsInChildren(true)) { customEmitter.Start(); } foreach (FMOD_StudioEventEmitter studioEventEmitter in escapePod.GetComponentsInChildren(true)) { studioEventEmitter.Start(); } MultiplayerCinematicReference reference = escapePod.EnsureComponent(); foreach (PlayerCinematicController controller in escapePod.GetComponentsInChildren(true)) { reference.AddController(controller); } } public bool SpawnsOwnChildren() => false; }