|
1 | 1 | using System.Reflection; |
2 | 2 | using NitroxClient.GameLogic; |
3 | 3 | using NitroxClient.MonoBehaviours; |
4 | | -using Nitrox.Model.Core; |
5 | 4 | using Nitrox.Model.DataStructures; |
6 | 5 | using Nitrox.Model.Subnautica.DataStructures.GameLogic.Entities; |
7 | 6 | using UnityEngine; |
8 | 7 |
|
9 | 8 | namespace NitroxPatcher.Patches.Dynamic; |
10 | 9 |
|
| 10 | +/// <summary> |
| 11 | +/// Handles the Sea Emperor baby hatching sequence in multiplayer. |
| 12 | +/// All players see the full hatching animation for better visual consistency. |
| 13 | +/// The simulating player spawns the real networked baby that will call SwimToMother(). |
| 14 | +/// Non-simulating players create temporary babies just for the animation sequence. |
| 15 | +/// </summary> |
11 | 16 | public sealed partial class IncubatorEgg_HatchNow_Patch : NitroxPatch, IDynamicPatch |
12 | 17 | { |
13 | 18 | private static readonly MethodInfo TARGET_METHOD = Reflect.Method((IncubatorEgg t) => t.HatchNow()); |
14 | 19 |
|
15 | 20 | public static bool Prefix(IncubatorEgg __instance) |
16 | 21 | { |
17 | | - StartHatchingVisuals(__instance); |
| 22 | + // Play hatching visual/sound effects for all players |
| 23 | + __instance.fxControl.Play(); |
| 24 | + Utils.PlayFMODAsset(__instance.hatchSound, __instance.transform, 30f); |
18 | 25 |
|
19 | | - SpawnBabiesIfSimulating(__instance); |
20 | | - |
21 | | - return false; |
22 | | - } |
23 | | - |
24 | | - private static void StartHatchingVisuals(IncubatorEgg egg) |
25 | | - { |
26 | | - egg.fxControl.Play(); |
27 | | - Utils.PlayFMODAsset(egg.hatchSound, egg.transform, 30f); |
| 26 | + NitroxEntity serverKnownParent = __instance.GetComponentInParent<NitroxEntity>(); |
| 27 | + if (!serverKnownParent) |
| 28 | + { |
| 29 | + Log.Error("Could not find a server known parent for incubator egg"); |
| 30 | + return false; |
| 31 | + } |
28 | 32 |
|
29 | | - SafeAnimator.SetBool(egg.animationController.eggAnimator, egg.animParameter, true); |
30 | | - } |
| 33 | + bool isSimulating = Resolve<SimulationOwnership>().HasAnyLockType(serverKnownParent.Id); |
31 | 34 |
|
32 | | - private static void SpawnBabiesIfSimulating(IncubatorEgg egg) |
33 | | - { |
34 | | - SimulationOwnership simulationOwnership = NitroxServiceLocator.LocateService<SimulationOwnership>(); |
| 35 | + // Create baby GameObject for animation (networked for simulating player, temporary for others) |
| 36 | + GameObject baby = Object.Instantiate(__instance.seaEmperorBabyPrefab); |
| 37 | + baby.transform.SetParent(__instance.attachPoint); |
| 38 | + baby.transform.localPosition = Vector3.zero; |
| 39 | + baby.transform.localRotation = Quaternion.identity; |
35 | 40 |
|
36 | | - NitroxEntity serverKnownParent = egg.GetComponentInParent<NitroxEntity>(); |
37 | | - Validate.NotNull(serverKnownParent, "Could not find a server known parent for incubator egg"); |
38 | | - |
39 | | - // Only spawn the babies if we are simulating the main incubator platform. |
40 | | - if (simulationOwnership.HasAnyLockType(serverKnownParent.Id)) |
| 41 | + if (isSimulating) |
41 | 42 | { |
42 | | - GameObject baby = UnityEngine.Object.Instantiate<GameObject>(egg.seaEmperorBabyPrefab); |
43 | | - baby.transform.position = egg.attachPoint.transform.position; |
44 | | - baby.transform.localRotation = Quaternion.identity; |
45 | | - |
| 43 | + // Simulating player: make this baby networked and broadcast it |
46 | 44 | NitroxId babyId = NitroxEntity.GenerateNewId(baby); |
47 | | - |
48 | | - WorldEntity entity = new(baby.transform.position.ToDto(), baby.transform.rotation.ToDto(), baby.transform.localScale.ToDto(), TechType.SeaEmperorBaby.ToDto(), 3, "09883a6c-9e78-4bbf-9561-9fa6e49ce766", false, babyId, null); |
| 45 | + WorldEntity entity = new( |
| 46 | + baby.transform.position.ToDto(), |
| 47 | + baby.transform.rotation.ToDto(), |
| 48 | + baby.transform.localScale.ToDto(), |
| 49 | + TechType.SeaEmperorBaby.ToDto(), |
| 50 | + 3, |
| 51 | + "09883a6c-9e78-4bbf-9561-9fa6e49ce766", |
| 52 | + false, |
| 53 | + babyId, |
| 54 | + null); |
49 | 55 | Resolve<Entities>().BroadcastEntitySpawnedByClient(entity); |
50 | 56 | } |
| 57 | + else |
| 58 | + { |
| 59 | + // Non-simulating player: mark baby as temporary (will be cleaned up after animation) |
| 60 | + baby.name += IncubatorEggAnimation_OnHatchAnimationEnd_Patch.TEMPORARY_BABY_MARKER; |
| 61 | + } |
| 62 | + |
| 63 | + // All players run the full animation sequence |
| 64 | + __instance.babyGO = baby; |
| 65 | + __instance.animationController.StartHatchAnimation(__instance.babyIdentifier, __instance.animParameter, baby); |
| 66 | + __instance.Invoke("PlayFxOnBaby", 2f); |
| 67 | + |
| 68 | + return false; |
51 | 69 | } |
52 | 70 | } |
0 commit comments