200 lines
7.4 KiB
C#
200 lines
7.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
// Одна строка таблицы рейтинга (ссылки на её элементы; заполняет RatingPanel)
|
|
[Serializable]
|
|
public class RatingRowView
|
|
{
|
|
public GameObject root;
|
|
public Image background;
|
|
public Image medal;
|
|
public TMP_Text placeText;
|
|
public Image avatar;
|
|
public TMP_Text avatarLetter;
|
|
public TMP_Text nameText;
|
|
public Image circleBanner;
|
|
public TMP_Text circleText;
|
|
public TMP_Text powerText;
|
|
public Button inspectButton;
|
|
}
|
|
|
|
// Панель "Рейтинг": разделы слева, таблица "Место / Игрок / Круг / Мощь", страницы внизу.
|
|
public class RatingPanel : MonoBehaviour
|
|
{
|
|
public const int PageSize = 10;
|
|
|
|
[Header("Общее")]
|
|
public Button closeButton;
|
|
public TMP_Text statusText;
|
|
|
|
[Header("Разделы (порядок как в RatingCategory)")]
|
|
public Button[] categoryButtons;
|
|
|
|
[Header("Таблица")]
|
|
public TMP_Text nameColumnHeader;
|
|
public TMP_Text circleColumnHeader;
|
|
public RatingRowView[] rows;
|
|
public TMP_Text emptyText;
|
|
|
|
[Header("Страницы")]
|
|
public Button prevPageButton;
|
|
public Button nextPageButton;
|
|
public Button[] pageButtons;
|
|
|
|
static readonly Color GoldRow = new Color(0.45f, 0.32f, 0.1f, 0.95f);
|
|
static readonly Color SilverRow = new Color(0.28f, 0.31f, 0.36f, 0.95f);
|
|
static readonly Color BronzeRow = new Color(0.36f, 0.22f, 0.12f, 0.95f);
|
|
static readonly Color EvenRow = new Color(0.07f, 0.09f, 0.13f, 0.95f);
|
|
static readonly Color OddRow = new Color(0.1f, 0.12f, 0.17f, 0.95f);
|
|
static readonly Color[] MedalColors = { new Color(0.95f, 0.75f, 0.3f), new Color(0.8f, 0.83f, 0.88f), new Color(0.8f, 0.5f, 0.28f) };
|
|
|
|
static readonly Color ActiveButton = new Color(0.16f, 0.35f, 0.7f);
|
|
static readonly Color InactiveButton = new Color(0.1f, 0.12f, 0.17f);
|
|
|
|
static readonly Color[] BannerColors =
|
|
{
|
|
new Color(0.15f, 0.35f, 0.75f), new Color(0.45f, 0.2f, 0.18f), new Color(0.7f, 0.15f, 0.15f),
|
|
new Color(0.2f, 0.45f, 0.2f), new Color(0.75f, 0.5f, 0.1f), new Color(0.35f, 0.35f, 0.4f)
|
|
};
|
|
|
|
private RatingCategory category = RatingCategory.Players;
|
|
private int pageIndex;
|
|
|
|
void Awake()
|
|
{
|
|
closeButton.onClick.AddListener(Close);
|
|
prevPageButton.onClick.AddListener(() => ShowPage(pageIndex - 1));
|
|
nextPageButton.onClick.AddListener(() => ShowPage(pageIndex + 1));
|
|
|
|
for (int i = 0; i < categoryButtons.Length; i++)
|
|
{
|
|
RatingCategory c = (RatingCategory)i;
|
|
categoryButtons[i].onClick.AddListener(() => SelectCategory(c));
|
|
}
|
|
|
|
foreach (var row in rows)
|
|
{
|
|
// Круглая медаль -- спрайт генерируется в коде, поэтому ставим его здесь, а не в сборщике сцены
|
|
row.medal.sprite = PrimitiveSprites.Circle;
|
|
}
|
|
}
|
|
|
|
public void Open()
|
|
{
|
|
gameObject.SetActive(true);
|
|
SelectCategory(category);
|
|
}
|
|
|
|
public void Close()
|
|
{
|
|
gameObject.SetActive(false);
|
|
}
|
|
|
|
void SelectCategory(RatingCategory newCategory)
|
|
{
|
|
category = newCategory;
|
|
for (int i = 0; i < categoryButtons.Length; i++)
|
|
categoryButtons[i].image.color = i == (int)category ? ActiveButton : InactiveButton;
|
|
|
|
bool circles = category == RatingCategory.Circles;
|
|
nameColumnHeader.text = circles ? "Круг" : "Игрок";
|
|
circleColumnHeader.text = circles ? "Глава" : "Круг";
|
|
|
|
ShowPage(0);
|
|
}
|
|
|
|
int PageCount => Mathf.Max(1, Mathf.CeilToInt(RatingService.Instance.GetTotalCount(category) / (float)PageSize));
|
|
|
|
void ShowPage(int newPageIndex)
|
|
{
|
|
pageIndex = Mathf.Clamp(newPageIndex, 0, PageCount - 1);
|
|
statusText.text = "";
|
|
|
|
bool available = RatingService.Instance.IsAvailable(category);
|
|
emptyText.gameObject.SetActive(!available);
|
|
emptyText.text = "Раздел в разработке";
|
|
|
|
List<RatingEntry> entries = available
|
|
? RatingService.Instance.GetPage(category, pageIndex, PageSize)
|
|
: new List<RatingEntry>();
|
|
|
|
for (int i = 0; i < rows.Length; i++)
|
|
{
|
|
if (i < entries.Count) FillRow(rows[i], entries[i], i);
|
|
else rows[i].root.SetActive(false);
|
|
}
|
|
|
|
UpdatePagination(available);
|
|
}
|
|
|
|
void FillRow(RatingRowView row, RatingEntry entry, int indexOnPage)
|
|
{
|
|
row.root.SetActive(true);
|
|
|
|
bool top3 = entry.place <= 3;
|
|
row.background.color = entry.place == 1 ? GoldRow
|
|
: entry.place == 2 ? SilverRow
|
|
: entry.place == 3 ? BronzeRow
|
|
: indexOnPage % 2 == 0 ? EvenRow : OddRow;
|
|
|
|
row.medal.gameObject.SetActive(top3);
|
|
if (top3) row.medal.color = MedalColors[entry.place - 1];
|
|
row.placeText.text = entry.place.ToString();
|
|
row.placeText.color = top3 ? new Color(0.2f, 0.12f, 0.05f) : Color.white;
|
|
|
|
// Аватар-заглушка: цветной квадрат с первой буквой имени
|
|
row.avatar.color = Color.Lerp(BannerColors[StableHash(entry.name) % BannerColors.Length], Color.black, 0.25f);
|
|
row.avatarLetter.text = entry.name.Substring(0, 1).ToUpper();
|
|
|
|
row.nameText.text = entry.name;
|
|
row.circleText.text = entry.circleName;
|
|
row.circleBanner.color = BannerColors[StableHash(category == RatingCategory.Circles ? entry.name : entry.circleName) % BannerColors.Length];
|
|
row.powerText.text = FormatPower(entry.power);
|
|
|
|
string name = entry.name;
|
|
row.inspectButton.onClick.RemoveAllListeners();
|
|
row.inspectButton.onClick.AddListener(() => statusText.text = $"Профиль «{name}» пока недоступен");
|
|
}
|
|
|
|
void UpdatePagination(bool available)
|
|
{
|
|
int pageCount = PageCount;
|
|
prevPageButton.interactable = available && pageIndex > 0;
|
|
nextPageButton.interactable = available && pageIndex < pageCount - 1;
|
|
|
|
// Окно из 5 номеров вокруг текущей страницы
|
|
int first = Mathf.Clamp(pageIndex - pageButtons.Length / 2, 0, Mathf.Max(0, pageCount - pageButtons.Length));
|
|
for (int i = 0; i < pageButtons.Length; i++)
|
|
{
|
|
int page = first + i;
|
|
Button button = pageButtons[i];
|
|
bool visible = available && page < pageCount;
|
|
button.gameObject.SetActive(visible);
|
|
if (!visible) continue;
|
|
|
|
button.GetComponentInChildren<TMP_Text>().text = (page + 1).ToString();
|
|
button.image.color = page == pageIndex ? ActiveButton : InactiveButton;
|
|
button.onClick.RemoveAllListeners();
|
|
button.onClick.AddListener(() => ShowPage(page));
|
|
}
|
|
}
|
|
|
|
// 128530000 -> "128 530 000"
|
|
static string FormatPower(long power)
|
|
{
|
|
return power.ToString("N0", CultureInfo.InvariantCulture).Replace(',', ' ');
|
|
}
|
|
|
|
// string.GetHashCode в Unity может меняться между запусками -- для цвета баннера нужен стабильный
|
|
static int StableHash(string s)
|
|
{
|
|
int hash = 17;
|
|
foreach (char c in s) hash = hash * 31 + c;
|
|
return hash & int.MaxValue;
|
|
}
|
|
}
|