Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
- доработан flow призовой витрины и связанного UI: добавлены `PrizeStandButtonView` и `RulesPrizeListPage`, обновлены `PrizeStandController`, `PrizeStandPrize`, `PrizeStandModal`, `PrizeStandSettingsModal`, `LevelPrizeSettingsModal`, `QRCodeSettingsModal`, `BalanceDisplay`, `ButtonTextFocusColor` и связанные prefab'ы, чтобы улучшить настройку, навигацию, отображение кнопок и prize flow - расширена система настроек и сохранения значений: `OptionPicker` получил отдельный формат `ValueSelector`, обновлены `SettingsDatabase`, `SettingEntryDrawer`, `SettingUIItem`, `SettingsUIGenerator`, `ValueSelectorRow` и `MainSettingsDatabase.asset`, а сохранение option picker-значений, включая `gameplay.gameDifficulty`, исправлено и синхронизировано с остальными настройками - добавлены универсальные спавнеры сущностей: реализованы `EntitySpawner` и `EntitySpawnerManager` с поддержкой весов префабов, точек спавна, заполнения по проценту, ограничений на количество и автоспавна при инициализации - доработана логика игрового спавна и сложности: процент инициализационного спавна может зависеть от настройки `gameplay.gameDifficulty` с градациями 25/50/75/100, в одном цикле массового спавна повторно не используется та же точка, а gizmo спавнера теперь показывает только сами точки без линий - упрощена работа в редакторе Unity и добавлены editor-инструменты: в `GameManager` лицензия пропускается при запуске в `UNITY_EDITOR`, а `HierarchySelectionCounter`, `HierarchySelectionCountOverlay` и `HierarchyBulkRenamerWindow` добавляют подсчет выделения, realtime overlay в `SceneView` и массовое переименование объектов по правилам нумерации из Unity Project Settings - синхронизированы сцены, ресурсы и UI-ассеты под новый flow: обновлены `MainMenu`, `Level UI`, уровни `1-1` и `1-2`, строки настроек, кнопки, TMP/font assets, изображения UI, `prizes.json` и recovery-данные после правок интерфейса и логики - очищены и переразложены часть внешних и служебных ресурсов: удалён пакет `Lava_Flowing_Shader`, переименованы/заменены отдельные UI-prefab'ы и добавлены сопутствующие проектные артефакты, которые сейчас используются обновлённой конфигурацией проекта
196 lines
6.7 KiB
C#
196 lines
6.7 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 => BuildQrPrizeText(manager, config.qrTier),
|
|
PrizeType.Hopper => $"хоппер, капсул: {Mathf.Max(1, config.hopperCapsuleCount)}",
|
|
PrizeType.PrizeStand => BuildPrizeStandText(manager),
|
|
_ => config.type.ToString()
|
|
};
|
|
}
|
|
|
|
private static string BuildQrPrizeText(PrizeSystemManager manager, QRTier tier)
|
|
{
|
|
string tierText = tier switch
|
|
{
|
|
QRTier.Bronze => "бронзовый QR-код",
|
|
QRTier.Silver => "серебряный QR-код",
|
|
QRTier.Gold => "золотой QR-код",
|
|
_ => "QR-код"
|
|
};
|
|
|
|
string prizeText = manager != null ? manager.GetQRCodeModalPrizeText(tier) : string.Empty;
|
|
return string.IsNullOrWhiteSpace(prizeText)
|
|
? tierText
|
|
: $"{tierText}: {prizeText.Trim()}";
|
|
}
|
|
|
|
private static string BuildPrizeStandText(PrizeSystemManager manager)
|
|
{
|
|
PrizeStandSettings settings = manager != null ? manager.GetPrizeStandSettings() : null;
|
|
int boxCount = settings?.boxes != null ? settings.boxes.Count : 0;
|
|
return boxCount > 0 ? $"призовая витрина, ячеек: {boxCount}" : "призовая витрина";
|
|
}
|
|
}
|
|
}
|