Danro/Assets/Scripts/UI/RulesPrizeListPage.cs
Danil Iarosh 77f7f9ef39
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
Use prize icons in rules page and align them vertically (with scale and position alignment)
- Добавлен Editor-скрипт SpriteAssetGenerator для автоматической генерации TMP Sprite Asset'ов из текстур призов.
- Сгенерированы TMP Sprite Asset'ы (Prize, Prize_Hopper, Prize_Marbles, Prize_QR, Prize_Tickets) в папке Assets/Resources/Sprite Assets.
- Настроены FaceInfo и вертикальное центрирование спрайтов относительно текста.
- Обновлен RulesPrizeListPage.cs: вместо текстовых названий призов выводятся соответствующие пиктограммы с помощью тегов <sprite>.
- Добавлены новые файлы текстур призов и их мета-файлы в коммит.
2026-08-03 16:42:43 +03:00

197 lines
7.0 KiB
C#

using System.Collections.Generic;
using System.IO;
using System.Text;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using RR.Common.Managers;
using RR.Common.Prizes;
namespace RR.Common.UI
{
/// <summary>
/// Страница правил, которая динамически выводит призы за уровни из настроек призовой системы.
/// Повесьте компонент на GameObject страницы и назначьте TMP_Text либо оставьте поле пустым,
/// чтобы компонент нашёл первый TMP_Text в дочерних объектах.
/// </summary>
[DisallowMultipleComponent]
public sealed class RulesPrizeListPage : MonoBehaviour
{
[Header("Text")]
[SerializeField] private TMP_Text targetText;
[Header("Content")]
[SerializeField] private string title = "Призы за уровни";
[SerializeField] private string emptyPrizeText = "без приза";
[SerializeField] private string noLevelsText = "Уровни не найдены.";
[SerializeField] private string unavailableText = "Настройки призов недоступны.";
[SerializeField] private bool showTitle = true;
[SerializeField] private bool showLevelsWithoutPrize = true;
[Header("Levels")]
[SerializeField] private bool includeOnlyLevelsFolder = true;
[SerializeField] private string levelsFolderName = "Levels";
[SerializeField] private List<string> ignoredSceneNames = new() { "MainMenu", "Level UI" };
private void Awake()
{
ResolveText();
}
private void OnEnable()
{
Refresh();
}
[ContextMenu("Refresh Prize List")]
public void Refresh()
{
ResolveText();
if (targetText == null)
{
Debug.LogWarning($"[{nameof(RulesPrizeListPage)}] TMP_Text не найден на странице правил.", this);
return;
}
PrizeSystemManager manager = PrizeSystemManager.Instance;
PrizeDatabase database = manager != null ? manager.Database : null;
if (database == null)
{
targetText.text = unavailableText;
return;
}
List<string> levels = GetLevelSceneNames();
if (levels.Count == 0)
{
levels.AddRange(database.GetAll().Keys);
levels.Sort();
}
if (levels.Count == 0)
{
targetText.text = noLevelsText;
return;
}
StringBuilder builder = new();
if (showTitle && !string.IsNullOrWhiteSpace(title))
{
builder.AppendLine(title);
builder.AppendLine();
}
for (int i = 0; i < levels.Count; i++)
{
string levelName = levels[i];
LevelPrizeConfig config = database.GetConfig(levelName);
if (!showLevelsWithoutPrize && (config == null || config.type == PrizeType.None))
{
continue;
}
builder.Append(levelName);
builder.Append(" — ");
builder.Append(BuildPrizeText(manager, config));
if (i < levels.Count - 1)
{
builder.AppendLine();
}
}
string result = builder.ToString().TrimEnd();
targetText.text = string.IsNullOrWhiteSpace(result) ? noLevelsText : result;
}
private void ResolveText()
{
if (targetText == null)
{
targetText = GetComponent<TMP_Text>();
}
if (targetText == null)
{
targetText = GetComponentInChildren<TMP_Text>(true);
}
}
private List<string> GetLevelSceneNames()
{
List<string> result = new();
int sceneCount = SceneManager.sceneCountInBuildSettings;
for (int i = 0; i < sceneCount; i++)
{
string scenePath = SceneUtility.GetScenePathByBuildIndex(i);
if (string.IsNullOrWhiteSpace(scenePath))
{
continue;
}
string normalizedPath = scenePath.Replace("\\", "/");
if (includeOnlyLevelsFolder)
{
string folderToken = $"/{levelsFolderName}/";
if (!normalizedPath.Contains(folderToken))
{
continue;
}
}
string sceneName = Path.GetFileNameWithoutExtension(scenePath);
if (ignoredSceneNames != null && ignoredSceneNames.Contains(sceneName))
{
continue;
}
result.Add(sceneName);
}
return result;
}
private string BuildPrizeText(PrizeSystemManager manager, LevelPrizeConfig config)
{
if (config == null || config.type == PrizeType.None)
{
return emptyPrizeText;
}
return config.type switch
{
PrizeType.QRCode => BuildQrPrizeSpriteText(manager, config.qrTier),
PrizeType.Hopper => $"<sprite=\"Prize_Hopper\" name=\"Prize_Hopper_0\"> x{Mathf.Max(1, config.hopperCapsuleCount)}",
PrizeType.PrizeStand => BuildPrizeStandSpriteText(manager),
_ => config.type.ToString()
};
}
private static string BuildQrPrizeSpriteText(PrizeSystemManager manager, QRTier tier)
{
string prizeText = manager != null ? manager.GetQRCodeModalPrizeText(tier) : string.Empty;
bool isTicket = !string.IsNullOrEmpty(prizeText) &&
(prizeText.IndexOf("билет", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
prizeText.IndexOf("ticket", System.StringComparison.OrdinalIgnoreCase) >= 0);
string spriteTag = isTicket
? "<sprite=\"Prize_Tickets\" name=\"Prize_Tickets_0\">"
: "<sprite=\"Prize_QR\" name=\"Prize_QR_0\">";
return string.IsNullOrWhiteSpace(prizeText)
? spriteTag
: $"{spriteTag} {prizeText.Trim()}";
}
private static string BuildPrizeStandSpriteText(PrizeSystemManager manager)
{
string spriteTag = "<sprite=\"Prize_Marbles\" name=\"Prize_Marbles_0\">";
PrizeStandSettings settings = manager != null ? manager.GetPrizeStandSettings() : null;
int boxCount = settings?.boxes != null ? settings.boxes.Count : 0;
return boxCount > 0 ? $"{spriteTag} (ячеек: {boxCount})" : spriteTag;
}
}
}