using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; namespace BlockSpace.Worlds { [DisallowMultipleComponent] public sealed class WorldLoadingScreenController : MonoBehaviour { private struct CameraState { public CameraClearFlags clearFlags; public Color backgroundColor; } private struct RigidbodyState { public Vector3 linearVelocity; public Vector3 angularVelocity; } [SerializeField] private GameObject loadingScreenObject; [SerializeField] private Slider progressSlider; [SerializeField] private bool hideSceneWhileLoading = true; [SerializeField] private int requiredTeleportsToComplete = 2; private readonly Dictionary rendererStates = new Dictionary(); private readonly Dictionary canvasStates = new Dictionary(); private readonly Dictionary graphicStates = new Dictionary(); private readonly Dictionary cameraStates = new Dictionary(); private readonly Dictionary movementBehaviourStates = new Dictionary(); private readonly Dictionary rigidbodyStates = new Dictionary(); private bool loading; private int completedTeleports; public static WorldLoadingScreenController Instance { get; private set; } public bool IsLoading => loading; public static event Action LoadingFinished; public void Configure(GameObject screenObject, Slider slider) { if (screenObject != null) { loadingScreenObject = screenObject; } if (slider != null) { progressSlider = slider; } if (!loading) { SetScreenVisible(visible: false); } } private void Awake() { if (Instance != null && Instance != this) { Debug.LogWarning("Multiple WorldLoadingScreenController instances found. Using the latest active instance.", this); } Instance = this; SetScreenVisible(visible: false); SetProgress(0f); } private void OnDisable() { SetLoading(isLoading: false); } private void OnDestroy() { if (Instance == this) { Instance = null; } } public void BeginLoading(float initialProgress = 0f) { completedTeleports = 0; SetLoading(isLoading: true); SetProgress(initialProgress); } public void SetLoading(bool isLoading) { if (loading == isLoading) { SetScreenVisible(isLoading); return; } loading = isLoading; SetScreenVisible(isLoading); if (hideSceneWhileLoading) { if (isLoading) { HideScene(); } else { RestoreScene(); } } if (isLoading) { DisableMovement(); } else { RestoreMovement(); } if (!isLoading) { completedTeleports = 0; SetProgress(0f); WorldLoadingScreenController.LoadingFinished?.Invoke(); } } public void SetCheckpoint(float progress) { if (loading && (!(progressSlider != null) || !(progress < progressSlider.value))) { SetProgress(progress); } } public void NotifyLocalPlayerTeleported(bool forceComplete = false) { if (loading) { completedTeleports++; if (!forceComplete && completedTeleports < Mathf.Max(1, requiredTeleportsToComplete)) { SetCheckpoint(0.97f); } else { CompleteLoading(); } } } public void CompleteLoading() { if (loading) { SetProgress(1f); SetLoading(isLoading: false); } } public void SetProgress(float progress) { if (loading && hideSceneWhileLoading) { HideScene(); } if (!(progressSlider == null)) { progressSlider.minValue = 0f; progressSlider.maxValue = 1f; progressSlider.value = Mathf.Clamp01(progress); } } private void SetScreenVisible(bool visible) { if (loadingScreenObject != null) { loadingScreenObject.SetActive(visible); } } private void HideScene() { Transform loadingRoot = ((loadingScreenObject != null) ? loadingScreenObject.transform : base.transform); Renderer[] array = UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); foreach (Renderer renderer in array) { if (!(renderer == null) && !rendererStates.ContainsKey(renderer) && !IsInsideLoadingScreen(renderer.transform, loadingRoot)) { rendererStates[renderer] = renderer.enabled; renderer.enabled = false; } } Canvas[] array2 = UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); foreach (Canvas canvas in array2) { if (!(canvas == null) && !canvasStates.ContainsKey(canvas) && !IsInsideLoadingScreen(canvas.transform, loadingRoot)) { canvasStates[canvas] = canvas.enabled; canvas.enabled = false; } } Graphic[] array3 = UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); foreach (Graphic graphic in array3) { if (!(graphic == null) && !graphicStates.ContainsKey(graphic) && !IsInsideLoadingScreen(graphic.transform, loadingRoot)) { graphicStates[graphic] = graphic.enabled; graphic.enabled = false; } } Camera[] array4 = UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); foreach (Camera camera in array4) { if (!(camera == null) && !cameraStates.ContainsKey(camera)) { cameraStates[camera] = new CameraState { clearFlags = camera.clearFlags, backgroundColor = camera.backgroundColor }; camera.clearFlags = CameraClearFlags.Color; camera.backgroundColor = Color.black; } } } private void RestoreScene() { foreach (KeyValuePair rendererState in rendererStates) { if (rendererState.Key != null) { rendererState.Key.enabled = rendererState.Value; } } foreach (KeyValuePair canvasState in canvasStates) { if (canvasState.Key != null) { canvasState.Key.enabled = canvasState.Value; } } foreach (KeyValuePair graphicState in graphicStates) { if (graphicState.Key != null) { graphicState.Key.enabled = graphicState.Value; } } foreach (KeyValuePair cameraState in cameraStates) { if (cameraState.Key != null) { cameraState.Key.clearFlags = cameraState.Value.clearFlags; cameraState.Key.backgroundColor = cameraState.Value.backgroundColor; } } rendererStates.Clear(); canvasStates.Clear(); graphicStates.Clear(); cameraStates.Clear(); } private void DisableMovement() { Behaviour[] array = UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); foreach (Behaviour behaviour in array) { if (!(behaviour == null) && !movementBehaviourStates.ContainsKey(behaviour) && IsMovementBehaviour(behaviour)) { movementBehaviourStates[behaviour] = behaviour.enabled; behaviour.enabled = false; } } Rigidbody[] array2 = UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); foreach (Rigidbody rigidbody in array2) { if (!(rigidbody == null) && !rigidbodyStates.ContainsKey(rigidbody)) { rigidbodyStates[rigidbody] = new RigidbodyState { linearVelocity = rigidbody.linearVelocity, angularVelocity = rigidbody.angularVelocity }; rigidbody.linearVelocity = Vector3.zero; rigidbody.angularVelocity = Vector3.zero; } } } private void RestoreMovement() { foreach (KeyValuePair movementBehaviourState in movementBehaviourStates) { if (movementBehaviourState.Key != null) { movementBehaviourState.Key.enabled = movementBehaviourState.Value; } } foreach (KeyValuePair rigidbodyState in rigidbodyStates) { if (rigidbodyState.Key != null) { rigidbodyState.Key.linearVelocity = rigidbodyState.Value.linearVelocity; rigidbodyState.Key.angularVelocity = rigidbodyState.Value.angularVelocity; } } movementBehaviourStates.Clear(); rigidbodyStates.Clear(); } private bool IsMovementBehaviour(Behaviour behaviour) { if (behaviour == this) { return false; } string text = behaviour.GetType().Name; if (!((behaviour.GetType().FullName ?? string.Empty) == "GorillaLocomotion.Player") && !(text == "DesktopRigController") && !(text == "CreatorFlightController") && !text.Contains("LocomotionProvider") && !text.Contains("MoveProvider") && !text.Contains("TurnProvider") && !text.Contains("JumpProvider") && !text.Contains("TeleportationProvider") && !text.Contains("ClimbProvider") && !text.Contains("ContinuousMove") && !text.Contains("DynamicMove") && !text.Contains("SnapTurn")) { return text.Contains("SmoothTurn"); } return true; } private bool IsInsideLoadingScreen(Transform target, Transform loadingRoot) { if (target != null && loadingRoot != null) { if (!(target == loadingRoot) && !target.IsChildOf(loadingRoot)) { return loadingRoot.IsChildOf(target); } return true; } return false; } } }