diff --git a/NitroxClient/GameLogic/PlayerLogic/PlayerModel/PlayerModelManager.cs b/NitroxClient/GameLogic/PlayerLogic/PlayerModel/PlayerModelManager.cs index 17b93539c3..10e5673a28 100644 --- a/NitroxClient/GameLogic/PlayerLogic/PlayerModel/PlayerModelManager.cs +++ b/NitroxClient/GameLogic/PlayerLogic/PlayerModel/PlayerModelManager.cs @@ -82,6 +82,13 @@ public IEnumerator AttachPing(INitroxPlayer player) // we also take a dependency on the lack of signalping later to differentiate remote player pings from others. Object.DestroyImmediate(signalBase.GetComponent()); + // Disable the SphereCollider on the signal to prevent it from triggering proximity events + // (e.g., precursor pedestals, doors) when the remote player approaches + if (signalBase.TryGetComponent(out SphereCollider sphereCollider)) + { + sphereCollider.enabled = false; + } + SetInGamePingColor(player, ping); } diff --git a/NitroxClient/GameLogic/RemotePlayer.cs b/NitroxClient/GameLogic/RemotePlayer.cs index 37afb034e5..526cfb246a 100644 --- a/NitroxClient/GameLogic/RemotePlayer.cs +++ b/NitroxClient/GameLogic/RemotePlayer.cs @@ -423,6 +423,7 @@ private void SetupBody() Collider.center = Vector3.zero; Collider.radius = refCollider.radius; + Collider.height = refCollider.height; Collider.direction = refCollider.direction; Collider.contactOffset = refCollider.contactOffset; Collider.isTrigger = true; diff --git a/NitroxPatcher/Patches/Dynamic/PrecursorActivatedPillar_OnTriggerEnter_Patch.cs b/NitroxPatcher/Patches/Dynamic/PrecursorActivatedPillar_OnTriggerEnter_Patch.cs new file mode 100644 index 0000000000..2406fbed96 --- /dev/null +++ b/NitroxPatcher/Patches/Dynamic/PrecursorActivatedPillar_OnTriggerEnter_Patch.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using NitroxClient.GameLogic.PlayerLogic; +using UnityEngine; + +namespace NitroxPatcher.Patches.Dynamic; + +/// +/// Makes ion cube pedestals (the ones with ion cubes on top that rise up) react to remote players. +/// +public sealed partial class PrecursorActivatedPillar_OnTriggerEnter_Patch : NitroxPatch, IDynamicPatch +{ + private static readonly MethodInfo TARGET_METHOD = Reflect.Method((PrecursorActivatedPillar t) => t.OnTriggerEnter(default)); + + public static bool Prefix(PrecursorActivatedPillar __instance, Collider col, bool ___active, ref bool ___extended, ref bool ___isFullyExtended, FMODAsset ___openSound, FMOD_CustomLoopingEmitter ___openedLoopingSound) + { + if (!___active) + { + return false; + } + + GameObject entityRoot = UWE.Utils.GetEntityRoot(col.gameObject); + if (!entityRoot) + { + entityRoot = col.gameObject; + } + + bool isLocalPlayer = entityRoot.GetComponentInChildren() != null; + bool isRemotePlayer = entityRoot.GetComponentInChildren() != null; + + if (!isLocalPlayer && !isRemotePlayer) + { + return false; + } + + // Track player count for proper exit handling + PrecursorActivatedPillar_OnTriggerExit_Patch.IncrementPlayerCount(__instance); + + // Only play sounds and animate if not already extended + if (!___extended) + { + if (___openSound) + { + Utils.PlayFMODAsset(___openSound, __instance.transform, 20f); + } + if (___openedLoopingSound) + { + ___openedLoopingSound.Play(); + } + ___extended = true; + ___isFullyExtended = false; + } + + return false; + } +} diff --git a/NitroxPatcher/Patches/Dynamic/PrecursorActivatedPillar_OnTriggerExit_Patch.cs b/NitroxPatcher/Patches/Dynamic/PrecursorActivatedPillar_OnTriggerExit_Patch.cs new file mode 100644 index 0000000000..daa5d38791 --- /dev/null +++ b/NitroxPatcher/Patches/Dynamic/PrecursorActivatedPillar_OnTriggerExit_Patch.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using System.Reflection; +using FMOD.Studio; +using NitroxClient.GameLogic.PlayerLogic; +using UnityEngine; + +namespace NitroxPatcher.Patches.Dynamic; + +/// +/// Prevents ion cube pedestals from retracting when a player leaves if other players are still nearby. +/// +public sealed partial class PrecursorActivatedPillar_OnTriggerExit_Patch : NitroxPatch, IDynamicPatch +{ + private static readonly MethodInfo TARGET_METHOD = Reflect.Method((PrecursorActivatedPillar t) => t.OnTriggerExit(default)); + + /// + /// Tracks the number of players currently in each pillar's trigger zone. + /// + private static readonly Dictionary playerCountByPillar = []; + + public static void IncrementPlayerCount(PrecursorActivatedPillar pillar) + { + int id = pillar.GetInstanceID(); + playerCountByPillar.TryGetValue(id, out int count); + playerCountByPillar[id] = count + 1; + } + + public static bool Prefix(PrecursorActivatedPillar __instance, Collider col, ref bool ___extended, ref bool ___isFullyExtended, FMODAsset ___closeSound, FMOD_CustomLoopingEmitter ___openedLoopingSound) + { + GameObject entityRoot = UWE.Utils.GetEntityRoot(col.gameObject); + if (!entityRoot) + { + entityRoot = col.gameObject; + } + + bool isLocalPlayer = entityRoot.GetComponentInChildren() != null; + bool isRemotePlayer = entityRoot.GetComponentInChildren() != null; + + if (!isLocalPlayer && !isRemotePlayer) + { + return false; + } + + // Decrement player count (only if we have a count for this pillar) + int id = __instance.GetInstanceID(); + int newCount = 0; + if (playerCountByPillar.TryGetValue(id, out int count) && count > 0) + { + newCount = count - 1; + playerCountByPillar[id] = newCount; + } + + // Only retract if no players remain + if (newCount <= 0) + { + if (___closeSound) + { + Utils.PlayFMODAsset(___closeSound, __instance.transform, 20f); + } + if (___openedLoopingSound) + { + ___openedLoopingSound.Stop(STOP_MODE.ALLOWFADEOUT); + } + ___extended = false; + ___isFullyExtended = true; + } + + return false; + } +} diff --git a/NitroxPatcher/Patches/Dynamic/PrecursorDisableGunTerminalArea_OnTriggerEnter_Patch.cs b/NitroxPatcher/Patches/Dynamic/PrecursorDisableGunTerminalArea_OnTriggerEnter_Patch.cs new file mode 100644 index 0000000000..23101b76da --- /dev/null +++ b/NitroxPatcher/Patches/Dynamic/PrecursorDisableGunTerminalArea_OnTriggerEnter_Patch.cs @@ -0,0 +1,41 @@ +using System.Reflection; +using NitroxClient.GameLogic.PlayerLogic; +using UnityEngine; + +namespace NitroxPatcher.Patches.Dynamic; + +/// +/// Makes the gun disable terminal open when remote players approach. +/// +public sealed partial class PrecursorDisableGunTerminalArea_OnTriggerEnter_Patch : NitroxPatch, IDynamicPatch +{ + private static readonly MethodInfo TARGET_METHOD = Reflect.Method((PrecursorDisableGunTerminalArea t) => t.OnTriggerEnter(default)); + + public static bool Prefix(PrecursorDisableGunTerminalArea __instance, Collider other) + { + GameObject entityRoot = UWE.Utils.GetEntityRoot(other.gameObject); + if (entityRoot == null) + { + entityRoot = other.gameObject; + } + + bool isLocalPlayer = entityRoot.GetComponent() != null; + bool isRemotePlayer = entityRoot.GetComponent() != null; + + if (!isLocalPlayer && !isRemotePlayer) + { + return false; + } + + // Track player count for proper exit handling + PrecursorDisableGunTerminalArea_OnTriggerExit_Patch.IncrementPlayerCount(__instance); + + // Only open if this is the first player entering + if (PrecursorDisableGunTerminalArea_OnTriggerExit_Patch.GetPlayerCount(__instance) == 1) + { + __instance.terminal.OnTerminalAreaEnter(); + } + + return false; + } +} diff --git a/NitroxPatcher/Patches/Dynamic/PrecursorDisableGunTerminalArea_OnTriggerExit_Patch.cs b/NitroxPatcher/Patches/Dynamic/PrecursorDisableGunTerminalArea_OnTriggerExit_Patch.cs new file mode 100644 index 0000000000..14b49d9f0c --- /dev/null +++ b/NitroxPatcher/Patches/Dynamic/PrecursorDisableGunTerminalArea_OnTriggerExit_Patch.cs @@ -0,0 +1,67 @@ +using System.Collections.Generic; +using System.Reflection; +using NitroxClient.GameLogic.PlayerLogic; +using UnityEngine; + +namespace NitroxPatcher.Patches.Dynamic; + +/// +/// Prevents the gun disable terminal from closing when a player leaves if other players are still nearby. +/// +public sealed partial class PrecursorDisableGunTerminalArea_OnTriggerExit_Patch : NitroxPatch, IDynamicPatch +{ + private static readonly MethodInfo TARGET_METHOD = Reflect.Method((PrecursorDisableGunTerminalArea t) => t.OnTriggerExit(default)); + + /// + /// Tracks the number of players currently in each trigger zone. + /// + private static readonly Dictionary playerCountByTrigger = []; + + public static void IncrementPlayerCount(PrecursorDisableGunTerminalArea trigger) + { + int id = trigger.GetInstanceID(); + playerCountByTrigger.TryGetValue(id, out int count); + playerCountByTrigger[id] = count + 1; + } + + public static int GetPlayerCount(PrecursorDisableGunTerminalArea trigger) + { + int id = trigger.GetInstanceID(); + playerCountByTrigger.TryGetValue(id, out int count); + return count; + } + + public static bool Prefix(PrecursorDisableGunTerminalArea __instance, Collider other) + { + GameObject entityRoot = UWE.Utils.GetEntityRoot(other.gameObject); + if (entityRoot == null) + { + entityRoot = other.gameObject; + } + + bool isLocalPlayer = entityRoot.GetComponent() != null; + bool isRemotePlayer = entityRoot.GetComponent() != null; + + if (!isLocalPlayer && !isRemotePlayer) + { + return false; + } + + // Decrement player count (only if we have a count for this trigger) + int id = __instance.GetInstanceID(); + int newCount = 0; + if (playerCountByTrigger.TryGetValue(id, out int count) && count > 0) + { + newCount = count - 1; + playerCountByTrigger[id] = newCount; + } + + // Only close if no players remain + if (newCount <= 0) + { + __instance.terminal.OnTerminalAreaExit(); + } + + return false; + } +} diff --git a/NitroxPatcher/Patches/Dynamic/PrecursorKeyTerminalTrigger_OnTriggerEnter_Patch.cs b/NitroxPatcher/Patches/Dynamic/PrecursorKeyTerminalTrigger_OnTriggerEnter_Patch.cs new file mode 100644 index 0000000000..18a06ecb21 --- /dev/null +++ b/NitroxPatcher/Patches/Dynamic/PrecursorKeyTerminalTrigger_OnTriggerEnter_Patch.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using NitroxClient.GameLogic.PlayerLogic; +using UnityEngine; + +namespace NitroxPatcher.Patches.Dynamic; + +/// +/// Makes precursor key terminal pedestals (tablet pedestals) react to remote players. +/// +public sealed partial class PrecursorKeyTerminalTrigger_OnTriggerEnter_Patch : NitroxPatch, IDynamicPatch +{ + private static readonly MethodInfo TARGET_METHOD = Reflect.Method((PrecursorKeyTerminalTrigger t) => t.OnTriggerEnter(default)); + + public static bool Prefix(PrecursorKeyTerminalTrigger __instance, Collider col) + { + bool isLocalPlayer = col.gameObject.Equals(Player.main.gameObject); + bool isRemotePlayer = col.GetComponentInParent() != null; + + if (!isLocalPlayer && !isRemotePlayer) + { + return false; + } + + // Track player count for proper exit handling + PrecursorKeyTerminalTrigger_OnTriggerExit_Patch.IncrementPlayerCount(__instance); + + // Only send OpenDeck if this is the first player entering + if (PrecursorKeyTerminalTrigger_OnTriggerExit_Patch.GetPlayerCount(__instance) == 1) + { + __instance.SendMessageUpwards("OpenDeck"); + } + + return false; + } +} diff --git a/NitroxPatcher/Patches/Dynamic/PrecursorKeyTerminalTrigger_OnTriggerExit_Patch.cs b/NitroxPatcher/Patches/Dynamic/PrecursorKeyTerminalTrigger_OnTriggerExit_Patch.cs new file mode 100644 index 0000000000..9a854eccd5 --- /dev/null +++ b/NitroxPatcher/Patches/Dynamic/PrecursorKeyTerminalTrigger_OnTriggerExit_Patch.cs @@ -0,0 +1,61 @@ +using System.Collections.Generic; +using System.Reflection; +using NitroxClient.GameLogic.PlayerLogic; +using UnityEngine; + +namespace NitroxPatcher.Patches.Dynamic; + +/// +/// Prevents precursor key terminal pedestals from closing when a player leaves if other players are still nearby. +/// +public sealed partial class PrecursorKeyTerminalTrigger_OnTriggerExit_Patch : NitroxPatch, IDynamicPatch +{ + private static readonly MethodInfo TARGET_METHOD = Reflect.Method((PrecursorKeyTerminalTrigger t) => t.OnTriggerExit(default)); + + /// + /// Tracks the number of players currently in each trigger zone. + /// + private static readonly Dictionary playerCountByTrigger = []; + + public static void IncrementPlayerCount(PrecursorKeyTerminalTrigger trigger) + { + int id = trigger.GetInstanceID(); + playerCountByTrigger.TryGetValue(id, out int count); + playerCountByTrigger[id] = count + 1; + } + + public static int GetPlayerCount(PrecursorKeyTerminalTrigger trigger) + { + int id = trigger.GetInstanceID(); + playerCountByTrigger.TryGetValue(id, out int count); + return count; + } + + public static bool Prefix(PrecursorKeyTerminalTrigger __instance, Collider col) + { + bool isLocalPlayer = col.gameObject.Equals(Player.main.gameObject); + bool isRemotePlayer = col.GetComponentInParent() != null; + + if (!isLocalPlayer && !isRemotePlayer) + { + return false; + } + + // Decrement player count (only if we have a count for this trigger) + int id = __instance.GetInstanceID(); + int newCount = 0; + if (playerCountByTrigger.TryGetValue(id, out int count) && count > 0) + { + newCount = count - 1; + playerCountByTrigger[id] = newCount; + } + + // Only close if no players remain + if (newCount <= 0) + { + __instance.SendMessageUpwards("CloseDeck"); + } + + return false; + } +}