using System; using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.InputSystem; using UnityEngine.UI; using RR.Common.Controls; using RR.Common.Managers; using RR.Game2D.Core.Managers; using ControlsBoxStatus = RR.Common.Controls.BoxStatus; namespace RR.Common.UI { /// /// Модальное окно витрины с выбором ячейки/полки для выдачи приза. /// Отображает сетку кнопок-ячеек. Навигация — через Input System (Navigate). /// public class PrizeStandModal : Modal { /// Текущий экземпляр на сцене. Регистрируется автоматически. public static PrizeStandModal Current { get; private set; } [Header("Контент")] [Tooltip("Контейнер для кнопок ячеек (например, GridLayoutGroup).")] [SerializeField] private Transform boxButtonsContainer; [Tooltip("Префаб кнопки ячейки. Должен содержать Button и TMP_Text.")] [SerializeField] private GameObject boxButtonPrefab; [Header("UI")] [Tooltip("Текст-заголовок окна (опционально).")] [SerializeField] private TMP_Text titleText; [Tooltip("Текст заголовка по умолчанию.")] [SerializeField] private string defaultTitle = "Выберите ячейку"; [Tooltip("Текст с количеством очков игрока.")] [SerializeField] private TMP_Text scoreText; [Tooltip("Формат текста очков. Используйте {0} для подстановки значения.")] [SerializeField] private string scoreTextFormat = "Очки за игру: {0}"; [Header("Навигация")] [Tooltip("InputAction для навигации (Navigate из Input System).")] [SerializeField] private InputActionReference navigateAction; /// /// Вызывается при выборе ячейки. Параметр — номер ячейки (1-based). /// Если игрок закрыл окно без выбора, вызывается с 0. /// public event Action OnBoxSelected; private readonly List _createdButtons = new(); private bool _selectionMade; private PrizeStandController _standController; private int _lastHighlightedBoxNumber; // ────────────── Lifecycle ────────────── protected override void Awake() { Current = this; base.Awake(); // При закрытии без выбора — уведомляем с 0 OnClosed += HandleClosedWithoutSelection; } private void OnDestroy() { if (Current == this) Current = null; OnClosed -= HandleClosedWithoutSelection; } // ────────────── Публичный API ────────────── /// /// Показывает окно с указанным количеством ячеек. /// /// Количество ячеек для отображения. public void Show( IReadOnlyList boxes, bool isConnected, int connectedBoxesCount, float gameTime, int finalScore, bool showFinalScore) { _selectionMade = false; _lastHighlightedBoxNumber = 0; _standController = isConnected ? ComSystemManager.GetPrizeStandController() : null; BuildButtons(boxes); if (titleText != null) { titleText.text = defaultTitle; } if (scoreText != null) { scoreText.text = BuildScoreText(finalScore); } Open(); GameManager.Instance?.SetPause(true); } // ────────────── Внутреннее ────────────── private void BuildButtons(IReadOnlyList boxes) { ClearButtons(); if (boxButtonsContainer == null || boxButtonPrefab == null) { Debug.LogError("[PrizeStandModal] Контейнер или префаб кнопки не назначены."); return; } if (boxes == null) return; for (int i = 0; i < boxes.Count; i++) { PrizeStandBoxAvailability box = boxes[i]; GameObject btnObj = Instantiate(boxButtonPrefab, boxButtonsContainer); btnObj.name = $"BoxButton_{box.BoxNumber}"; PrizeStandButtonView buttonView = btnObj.GetComponent(); if (buttonView == null) { buttonView = btnObj.AddComponent(); } buttonView.Bind(box.BoxNumber, box.requiredScore); // Настраиваем кнопку Button btn = btnObj.GetComponent