using UdonSharp; using UnityEngine; using VRC.SDK3.UdonNetworkCalling; using VRC.SDKBase; using VRC.Udon; using VRC.Udon.Common.Interfaces; [UdonBehaviourSyncMode(BehaviourSyncMode.Manual)] public class Modem : UdonSharpBehaviour { [UdonSynced, FieldChangeCallback(nameof(ActivateModem))] private bool _ActivateModem = false; [UdonSynced, FieldChangeCallback(nameof(BeginTeleport))] private bool _BeginTeleport = false; [SerializeField] private GameObject _ModemActivationObject; [SerializeField] private Modem _ModemDestination; [Space] [SerializeField] private AudioManager _AudioManager; [SerializeField] private Animator _ModemAnimator; [SerializeField] private Animator _ModemDestinationAnimator; [UdonSynced] private string[] _EnteredPlayers = new string[MAX_PLAYERS_IN_MODEM]; private const int MAX_PLAYERS_IN_MODEM = 10; public override void OnPlayerTriggerEnter(VRCPlayerApi Player) { for (int i = 0; i < MAX_PLAYERS_IN_MODEM; i++) { if (_EnteredPlayers[i] == null) { _EnteredPlayers[i] = Player.displayName; break; } } RequestSerialization(); base.OnPlayerTriggerEnter(Player); } public override void OnPlayerTriggerExit(VRCPlayerApi Player) { for (int i = 0; i < MAX_PLAYERS_IN_MODEM; i++) { if (_EnteredPlayers[i] == Player.displayName) { _EnteredPlayers[i] = null; break; } } RequestSerialization(); base.OnPlayerTriggerExit(Player); } public void Activate(bool Active) { ActivateModem = Active; RequestSerialization(); } public void Teleport() { BeginTeleport = true; RequestSerialization(); } private void ExecuteTeleportProcess() { // This is a synced function, so only play the sound effect locally. _AudioManager.PlaySFX(SFXEventType.ModemOperation); _ModemAnimator.SetBool("Teleport", true); _ModemDestinationAnimator.SetBool("Teleport", true); SendCustomEventDelayedSeconds(nameof(TeleportAllPlayers), 4.0f); SendCustomEventDelayedSeconds(nameof(EndTeleport), 6.0f); } public void TeleportAllPlayers() { // Only the Modem owner should execute teleportations. if (Networking.GetOwner(gameObject) == Networking.LocalPlayer) { SendCustomNetworkEvent(NetworkEventTarget.All, nameof(TeleportLocalPlayerIfInModem)); } } public void ResetTeleportProcess() { _ModemAnimator.SetBool("Teleport", false); _ModemDestinationAnimator.SetBool("Teleport", false); } public void EndTeleport() { BeginTeleport = false; RequestSerialization(); } [NetworkCallable] public void TeleportLocalPlayerIfInModem() { for (int j = 0; j < _EnteredPlayers.Length; j++) { if (_EnteredPlayers[j] == Networking.LocalPlayer.displayName) { TeleportToModemDestination(); break; } } } private void TeleportToModemDestination() { Vector3 DeltaVector = Networking.LocalPlayer.GetPosition() - transform.position; Quaternion DeltaRotation = _ModemDestination.transform.rotation * Quaternion.Inverse(transform.rotation); Vector3 DestinationVectorRotated = DeltaRotation * DeltaVector.normalized; Vector3 DestinationPositionDelta = DestinationVectorRotated * DeltaVector.magnitude; Networking.LocalPlayer.TeleportTo( _ModemDestination.transform.position + DestinationPositionDelta, DeltaRotation * Networking.LocalPlayer.GetRotation()); } private bool ActivateModem { set { _ActivateModem = value; _ModemActivationObject.SetActive(_ActivateModem); } get => _ActivateModem; } private bool BeginTeleport { set { _BeginTeleport = value; if (_BeginTeleport) { ExecuteTeleportProcess(); } else { ResetTeleportProcess(); } } get => _BeginTeleport; } }