using System; using System.Collections; using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; using TMPro; using RR.Common.Settings; using RR.Game2D.Core.Managers; namespace RR.Common.UI { /// /// Контроллер баннера проигрыша. /// Модальное окно, которое отображается при поражении игрока. /// public class LoseBannerController : Modal { [Header("Элементы баннера проигрыша")] [SerializeField] private TextMeshProUGUI loseText; [SerializeField] private TextMeshProUGUI creditText; [SerializeField] private Button continueButton; [SerializeField] private TextMeshProUGUI continueButtonText; [SerializeField] private Button menuButton; [SerializeField] private TextMeshProUGUI menuButtonText; [SerializeField] private float continueButtonCountdown = 30f; [Header("Тексты")] [SerializeField] private string creditTextFormat = "У вас осталось: {0}"; [SerializeField] private string continueButtonTextFormat = "Продолжить? ({0})"; [SerializeField] private string menuButtonTextFormat = "В меню ({0})"; private RR.Common.Managers.BalanceManager _balanceManager; private Coroutine _continueCountdownCoroutine; private bool _isSubscribedToBalance = false; /// /// Событие, вызываемое при нажатии "Продолжить" или закрытии баннера. /// public event Action ContinueClicked; protected override void Awake() { base.Awake(); if (continueButton) { continueButton.onClick.AddListener(OnContinueButtonClicked); } if (menuButton) { menuButton.onClick.AddListener(OnMenuButtonClicked); } } protected override void OnEnable() { base.OnEnable(); // Инициализируем BalanceManager if (_balanceManager == null) { _balanceManager = GameManager.Instance?.Balance; } // Подписываемся на событие только если еще не подписаны if (_balanceManager != null && !_isSubscribedToBalance) { int balance = _balanceManager.CurrentBalance; if (creditText) { creditText.text = string.Format(creditTextFormat, balance); } _balanceManager.OnBalanceChanged.AddListener(UnlockContinueButton); _isSubscribedToBalance = true; } } protected override void OnDisable() { // Остановим корутину, если объект деактивируется if (_continueCountdownCoroutine != null) { StopCoroutine(_continueCountdownCoroutine); _continueCountdownCoroutine = null; } // Безопасная отписка от события if (_balanceManager != null && _isSubscribedToBalance) { _balanceManager.OnBalanceChanged.RemoveListener(UnlockContinueButton); _isSubscribedToBalance = false; } base.OnDisable(); } /// /// Показывает баннер проигрыша с указанным временем игры. /// /// Время игры в секундах public void ShowLose(float gameTime) { // Инициализируем BalanceManager до открытия окна if (_balanceManager == null) { _balanceManager = GameManager.Instance?.Balance; } // Подписываемся на событие изменения баланса, если еще не подписаны if (_balanceManager != null && !_isSubscribedToBalance) { _balanceManager.OnBalanceChanged.AddListener(UnlockContinueButton); _isSubscribedToBalance = true; } if (loseText) { loseText.text = $"Время игры: {TimeFormatHelper.FormatTime(gameTime)}"; } if (creditText && _balanceManager != null) { creditText.text = string.Format(creditTextFormat, (object)_balanceManager.CurrentBalance); } if (continueButtonCountdown <= 0f) { ReturnToMainMenu(); return; } Button activeButton = ConfigureVisibleButtons(); if (activeButton != null) { SetFirstSelected(activeButton); } Open(); FocusActiveButton(activeButton); // Остановим предыдущую корутину, если есть if (_continueCountdownCoroutine != null) { StopCoroutine(_continueCountdownCoroutine); } _continueCountdownCoroutine = StartCoroutine(ContinueCountdownCoroutine(continueButtonCountdown)); } private IEnumerator ContinueCountdownCoroutine(float duration) { float elapsed = 0f; int initialSeconds = Mathf.RoundToInt(duration); UpdateContinueText(initialSeconds); while (elapsed < duration) { float startTime = Time.unscaledTime; yield return null; while (Time.unscaledTime - startTime < 1.0f) { yield return null; } elapsed += 1f; int remaining = Mathf.Max(0, Mathf.RoundToInt(duration - elapsed)); UpdateContinueText(remaining); } // Таймер истёк — закрываем баннер и выходим в меню Close(); ContinueClicked?.Invoke(); if (GameManager.Instance) { GameManager.Instance.SetPause(false); GameManager.Instance.LoadScene("MainMenu", false); } _continueCountdownCoroutine = null; } private void UpdateContinueText(int secondsRemaining) { if (continueButton != null && continueButton.gameObject.activeSelf && continueButtonText) { continueButtonText.text = string.Format(continueButtonTextFormat, secondsRemaining); } if (menuButton != null && menuButton.gameObject.activeSelf && menuButtonText) { menuButtonText.text = string.Format(menuButtonTextFormat, secondsRemaining); } } private void UnlockContinueButton(int newBalance) { if (_balanceManager == null) return; // Проверяем возможность продолжить через BalanceManager if (continueButton) { continueButton.interactable = _balanceManager.CanContinueGame(); } // Обновляем текст баланса if (creditText) { creditText.text = string.Format(creditTextFormat, newBalance); } } private void OnContinueButtonClicked() { // При клике на "Продолжить" — остановим таймер и продолжим игру if (_continueCountdownCoroutine != null) { StopCoroutine(_continueCountdownCoroutine); _continueCountdownCoroutine = null; } Close(); ContinueClicked?.Invoke(); if (GameManager.Instance) { GameManager.Instance.SetPause(false); GameManager.Instance.ContinueGame(); } } private void OnMenuButtonClicked() { if (_continueCountdownCoroutine != null) { StopCoroutine(_continueCountdownCoroutine); _continueCountdownCoroutine = null; } ReturnToMainMenu(); } private Button ConfigureVisibleButtons() { bool canContinueGameSetting = SettingsManager.Instance != null && SettingsManager.Instance.GetBool("gameplay.canContinueGame"); if (continueButton != null) { continueButton.gameObject.SetActive(canContinueGameSetting); continueButton.interactable = canContinueGameSetting && _balanceManager != null && _balanceManager.CanContinueGame(); } if (menuButton != null) { menuButton.gameObject.SetActive(!canContinueGameSetting); menuButton.interactable = true; } return canContinueGameSetting ? continueButton : menuButton; } private void FocusActiveButton(Button activeButton) { if (activeButton == null || !activeButton.gameObject.activeInHierarchy || EventSystem.current == null) { return; } EventSystem.current.SetSelectedGameObject(null); EventSystem.current.SetSelectedGameObject(activeButton.gameObject); activeButton.Select(); } private void ReturnToMainMenu() { Close(); ContinueClicked?.Invoke(); if (GameManager.Instance) { GameManager.Instance.SetPause(false); GameManager.Instance.LoadScene("MainMenu", false); } } } }