165 lines
5.7 KiB
C#
165 lines
5.7 KiB
C#
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<RatingModal>(FindObjectsInactive.Include);
|
||
|
||
if (_modal == null)
|
||
{
|
||
Debug.LogWarning("Не найдено модальное окно рейтинга.");
|
||
return;
|
||
}
|
||
|
||
if (_modal.IsOpen)
|
||
{
|
||
_modal.Close();
|
||
return;
|
||
}
|
||
|
||
Canvas canvas = root.GetComponentInParent<Canvas>()?.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<PlayerStatsRecord> records = statsService?.GetTopPlayers(_recordsCount)
|
||
?? new List<PlayerStatsRecord>();
|
||
RectTransform prefabRect = _recordPrefab.GetComponent<RectTransform>();
|
||
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<RectTransform>();
|
||
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<TextMeshProUGUI>(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}";
|
||
}
|
||
|
||
}
|