using System.Collections; using System.Collections.Generic; using DG.Tweening; using UnityEngine; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; using Bansonic; public class UI_SongsSelect_RuntimeFeatures : MonoBehaviour { [Header("Scene")] [SerializeField] private string requiredSceneName = "Songs_Select"; [SerializeField] private string gameplaySceneName = "gamePlay_gamePlay"; [Header("Targets")] [SerializeField] private string enterButtonName = "bottom"; [SerializeField] private string enterButtonParentName = "enterBar"; [SerializeField] private string autoPlayToggleName = "enableAutoPlayToggle"; [SerializeField] private string difficultiesRootName = "horizontalDifficultiesLayouts"; [Header("Difficulty Indicator Pop")] [SerializeField] private float diffPopScaleFrom = 1.25f; [SerializeField] private float diffPopTime = 0.26f; [SerializeField] private float diffPopFadeInTime = 0.08f; [SerializeField] private Ease diffPopEase = Ease.OutBack; [Header("Difficulty ID Roll")] [SerializeField] private string difficultyIdName = "difficultyID"; [SerializeField] private string difficultyIdGroundName = "difficultyIDGround"; [SerializeField] private float difficultyIdStepDelayFast = 0.03f; [SerializeField] private float difficultyIdStepDelaySlow = 0.11f; [SerializeField] private bool difficultyIdUseUnscaledTime = true; private Button enterButton; private UnityAction enterAction; private Toggle autoPlayToggle; private UnityAction autoPlayToggleAction; private Coroutine bindRoutine; private Coroutine bgmKeepAliveRoutine; private Coroutine difficultyIdRollCoroutine; private readonly Dictionary diffActions = new Dictionary(); private readonly Dictionary> diffToggleActions = new Dictionary>(); private readonly List difficultyIdDisplayTexts = new List(); private int difficultyIdDisplayed = int.MinValue; private float nextDifficultyIdRefreshTime = 0f; private void OnEnable() { var bgm = BgmPlaybackManager.Instance ?? BgmPlaybackManager.EnsureInstance(); if (bgm != null) { bgm.autoPlay = true; if (bgm.audioSource != null) { bgm.audioSource.mute = false; if (bgm.audioSource.volume <= 0.001f) bgm.audioSource.volume = 1f; } bgm.EnsurePlaying(); } if (bindRoutine != null) { StopCoroutine(bindRoutine); bindRoutine = null; } if (bgmKeepAliveRoutine != null) StopCoroutine(bgmKeepAliveRoutine); bgmKeepAliveRoutine = StartCoroutine(EnsureBgmAliveRoutine()); RebindSoon(); ResolveDifficultyIdDisplayTexts(); RefreshDifficultyIdDisplay(forceInstant: true); } private void OnDisable() { if (bindRoutine != null) { StopCoroutine(bindRoutine); bindRoutine = null; } if (bgmKeepAliveRoutine != null) { StopCoroutine(bgmKeepAliveRoutine); bgmKeepAliveRoutine = null; } if (difficultyIdRollCoroutine != null) { StopCoroutine(difficultyIdRollCoroutine); difficultyIdRollCoroutine = null; } Unbind(); } private void Update() { if (!IsInRequiredScene()) return; if (Time.unscaledTime < nextDifficultyIdRefreshTime) return; nextDifficultyIdRefreshTime = Time.unscaledTime + 0.06f; RefreshDifficultyIdDisplay(forceInstant: false); } private IEnumerator EnsureBgmAliveRoutine() { while (enabled) { if (IsInRequiredScene()) { var bgm = BgmPlaybackManager.Instance ?? BgmPlaybackManager.EnsureInstance(); if (bgm != null) { bgm.autoPlay = true; if (bgm.audioSource != null) { bgm.audioSource.mute = false; if (bgm.audioSource.volume <= 0.001f) bgm.audioSource.volume = 1f; } bgm.EnsurePlaying(); } } yield return new WaitForSecondsRealtime(0.25f); } bgmKeepAliveRoutine = null; } public void RebindSoon() { if (bindRoutine != null) StopCoroutine(bindRoutine); bindRoutine = StartCoroutine(BindRoutine()); } private IEnumerator BindRoutine() { // Wait a few frames so scene-internal scripts finish wiring first. yield return null; TryBindAll(); yield return null; TryBindAll(); yield return null; TryBindAll(); bindRoutine = null; } private void TryBindAll() { if (!IsInRequiredScene()) return; DisableDecorativeDifficultyRaycasts(); ResolveDifficultyIdDisplayTexts(); BindEnterButton(); BindAutoPlayToggle(); BindDifficultyButtons(); RefreshDifficultyIdDisplay(forceInstant: false); } private bool IsInRequiredScene() { Scene scene = gameObject.scene; if (!scene.IsValid() || !scene.isLoaded) scene = SceneManager.GetActiveScene(); return string.IsNullOrEmpty(requiredSceneName) || scene.name == requiredSceneName; } private void BindEnterButton() { Button next = FindEnterButton(); if (next == null) return; if (enterButton != null && enterAction != null && enterButton != next) enterButton.onClick.RemoveListener(enterAction); enterButton = next; if (enterAction == null) enterAction = OnEnterGameplayClicked; enterButton.onClick.RemoveListener(enterAction); enterButton.onClick.AddListener(enterAction); } private void BindAutoPlayToggle() { Transform tr = FindInCurrentSceneByName(autoPlayToggleName); if (tr == null) return; Toggle toggle = tr.GetComponent(); if (toggle == null) return; if (autoPlayToggle != null && autoPlayToggleAction != null && autoPlayToggle != toggle) autoPlayToggle.onValueChanged.RemoveListener(autoPlayToggleAction); autoPlayToggle = toggle; if (autoPlayToggleAction == null) autoPlayToggleAction = OnAutoPlayToggleChanged; autoPlayToggle.onValueChanged.RemoveListener(autoPlayToggleAction); autoPlayToggle.onValueChanged.AddListener(autoPlayToggleAction); autoPlayToggle.SetIsOnWithoutNotify(GameConfig.autoPlayEnabled); EnsureSelectableClickable(autoPlayToggle); } private void OnAutoPlayToggleChanged(bool isOn) { GameConfig.SetAutoPlayEnabled(isOn); } private void BindDifficultyButtons() { foreach (var kv in diffActions) { if (kv.Key != null && kv.Value != null) kv.Key.onClick.RemoveListener(kv.Value); } diffActions.Clear(); foreach (var kv in diffToggleActions) { if (kv.Key != null && kv.Value != null) kv.Key.onValueChanged.RemoveListener(kv.Value); } diffToggleActions.Clear(); Transform root = FindInCurrentSceneByName(difficultiesRootName); if (root == null) return; EnsureAncestorCanvasGroupsAllowInput(root); Button[] buttons = root.GetComponentsInChildren