using DG.Tweening; using System.Collections.Generic; using TMPro; using UnityEngine; public class RatingPanel : InfoPanel { [Header("Описание")] [SerializeField] private RectTransform descriptionSign; [SerializeField] private float descriptionOpenY, descriptionCloseY, descriptionDuration; [Header("Топ рейтинга")] [SerializeField] private GameObject _recordPrefab; [SerializeField] private int _recordsCount = 3; [SerializeField] private Vector2 _recordsSize = new(800f, 190f); [SerializeField] private float _recordSpacing = 4f; private RatingModal _modal; private RectTransform _recordsRoot; private const string DateFormat = "dd.MM.yy"; public override bool BlocksNavigation => _modal != null && _modal.IsOpen; public override float closeDelay => (infoDuration + buttonDuration + descriptionDuration - timeOffset * 3 + titleDuration) / closeSpeedMultiplier; public override void Initialize() { } public override void Interact() { if (_modal == null) _modal = FindFirstObjectByType(FindObjectsInactive.Include); if (_modal == null) { Debug.LogWarning("Не найдено модальное окно рейтинга."); return; } if (_modal.IsOpen) { _modal.Close(); return; } Canvas canvas = root.GetComponentInParent()?.rootCanvas; _modal.Open(canvas != null ? canvas.transform : null); } public override void CloseModal() { _modal?.Close(); } protected override void InitializeBeforeOpen() { BuildTopRecords(); } protected override Sequence GetOpenSequence(float delay) { Sequence seq = DOTween.Sequence(); seq.AppendInterval(delay); seq.Append(root.DOAnchorPos(Vector2.zero, titleDuration)); seq.Append(titleSign.DOAnchorPosY(titleOpenY, titleDuration)); float t = delay + titleDuration * 2; seq.Insert(t, descriptionSign.DOAnchorPosY(descriptionOpenY, descriptionDuration)); seq.Insert(t, button.DOAnchorPosX(buttonOpenX, buttonDuration)); seq.Insert(t + buttonDuration - timeOffset, info.DOAnchorPosX(infoOpenX, infoDuration).SetEase(Ease.OutBack)); return seq; } protected override Sequence GetCloseSequence(Vector2 position) { Sequence seq = DOTween.Sequence(); float time = 0; seq.Insert(time, info.DOAnchorPosX(infoCloseX, infoDuration)); time += infoDuration - timeOffset; seq.Insert(time, button.DOAnchorPosX(buttonCloseX, buttonDuration)); time += buttonDuration - timeOffset; seq.Insert(time, descriptionSign.DOAnchorPosY(descriptionCloseY, descriptionDuration)); time += descriptionDuration - timeOffset; seq.Insert(time, titleSign.DOAnchorPosY(titleCloseY, titleDuration)); seq.Append(root.DOAnchorPos(position, titleDuration * 4)); seq.timeScale = closeSpeedMultiplier; return seq; } private void BuildTopRecords() { _recordsRoot ??= transform.Find("Info/RecordsGroup") as RectTransform; if (_recordsRoot == null || _recordPrefab == null) { Debug.LogWarning("Не настроен вывод топа рейтинга в главном меню."); return; } ClearTopRecords(); _recordsRoot.sizeDelta = _recordsSize; PlayerStatsService statsService = GameManager.Instance?.StatsService; List records = statsService?.GetTopPlayers(_recordsCount) ?? new List(); RectTransform prefabRect = _recordPrefab.GetComponent(); float prefabWidth = prefabRect != null ? prefabRect.rect.width : _recordsRoot.rect.width; float prefabHeight = prefabRect != null ? prefabRect.rect.height : 66f; float scale = prefabWidth > 0f ? _recordsRoot.rect.width / prefabWidth : 1f; float rowHeight = prefabHeight * scale; float rowStep = rowHeight + _recordSpacing; float firstRowY = (records.Count - 1) * rowStep * 0.5f; for (int i = 0; i < records.Count; i++) { RectTransform row = Instantiate(_recordPrefab, _recordsRoot).GetComponent(); row.anchorMin = new Vector2(0.5f, 0.5f); row.anchorMax = new Vector2(0.5f, 0.5f); row.pivot = new Vector2(0.5f, 0.5f); row.anchoredPosition = new Vector2(0f, firstRowY - i * rowStep); row.localScale = Vector3.one * scale; FillTopRecord(row, i + 1, records[i]); } } private void ClearTopRecords() { for (int i = _recordsRoot.childCount - 1; i >= 0; i--) { GameObject child = _recordsRoot.GetChild(i).gameObject; child.SetActive(false); Destroy(child); } } private void FillTopRecord(RectTransform row, int rank, PlayerStatsRecord record) { TextMeshProUGUI[] cells = row.GetComponentsInChildren(true); if (cells.Length < 5) return; cells[0].text = rank.ToString(); cells[1].text = record.PlayerName; cells[2].text = FormatTime(record.CompletionTimeSec); cells[3].text = record.MovesCount.ToString(); cells[4].text = record.CompletedAt.ToString(DateFormat); } private string FormatTime(float seconds) { int totalSeconds = Mathf.Max(0, Mathf.RoundToInt(seconds)); return $"{totalSeconds / 60:D2}:{totalSeconds % 60:D2}"; } }