Danro/Assets/Scripts/UI/PrizeStandButtonView.cs
Danil Iarosh cb2e83a596
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
feat(ui, prizes, spawners, editor): expand prize flow and add spawning/editor tooling
- доработан 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'ы и добавлены сопутствующие проектные артефакты,
  которые сейчас используются обновлённой конфигурацией проекта
2026-04-28 14:05:05 +03:00

159 lines
5.1 KiB
C#

using TMPro;
using UnityEngine;
namespace RR.Common.UI
{
/// <summary>
/// Отображение кнопки ячейки витрины.
/// Поддерживает отдельные текстовые поля для номера ячейки и цены в очках.
/// Если префаб ещё не обновлён, недостающие поля будут созданы автоматически.
/// </summary>
[DisallowMultipleComponent]
public sealed class PrizeStandButtonView : MonoBehaviour
{
[Header("Labels")]
[SerializeField] private TMP_Text boxNumberText;
[SerializeField] private TMP_Text boxPriceText;
[Header("Formatting")]
[SerializeField] private string boxPriceFormat = "{0}";
public void Bind(int boxNumber, int requiredScore)
{
EnsureTextFields();
if (boxNumberText != null)
{
boxNumberText.text = boxNumber.ToString();
}
if (boxPriceText != null)
{
boxPriceText.text = FormatPrice(requiredScore);
}
}
private void EnsureTextFields()
{
if (boxNumberText != null && boxPriceText != null)
{
return;
}
bool createdMissingFields = false;
TMP_Text[] texts = GetComponentsInChildren<TMP_Text>(true);
if (boxNumberText == null && texts.Length > 0)
{
boxNumberText = texts[0];
}
if (boxPriceText == null)
{
for (int i = 0; i < texts.Length; i++)
{
if (texts[i] != null && texts[i] != boxNumberText)
{
boxPriceText = texts[i];
break;
}
}
}
if (boxNumberText == null)
{
boxNumberText = CreateTextField("BoxNumberText");
createdMissingFields = true;
}
if (boxPriceText == null)
{
boxPriceText = CreateTextField("BoxPriceText", boxNumberText);
createdMissingFields = true;
}
if (createdMissingFields)
{
ApplyDefaultLayout();
}
}
private TMP_Text CreateTextField(string objectName, TMP_Text template = null)
{
GameObject textObject = new(objectName, typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI));
textObject.transform.SetParent(transform, false);
textObject.layer = gameObject.layer;
TMP_Text created = textObject.GetComponent<TMP_Text>();
if (template != null)
{
created.font = template.font;
created.fontSharedMaterial = template.fontSharedMaterial;
created.fontSize = template.fontSize;
created.fontSizeMin = template.fontSizeMin;
created.fontSizeMax = template.fontSizeMax;
created.enableAutoSizing = template.enableAutoSizing;
created.color = template.color;
created.alignment = template.alignment;
created.raycastTarget = template.raycastTarget;
}
else
{
created.enableAutoSizing = true;
created.fontSizeMin = 10;
created.fontSizeMax = 36;
created.color = Color.black;
created.alignment = TextAlignmentOptions.Center;
}
return created;
}
private void ApplyDefaultLayout()
{
if (boxNumberText != null)
{
ConfigureTextRect(boxNumberText.rectTransform, new Vector2(0f, 0.48f), new Vector2(1f, 1f));
boxNumberText.alignment = TextAlignmentOptions.Center;
}
if (boxPriceText != null)
{
ConfigureTextRect(boxPriceText.rectTransform, new Vector2(0f, 0f), new Vector2(1f, 0.48f));
boxPriceText.alignment = TextAlignmentOptions.Center;
}
}
private static void ConfigureTextRect(RectTransform rectTransform, Vector2 anchorMin, Vector2 anchorMax)
{
if (rectTransform == null)
{
return;
}
rectTransform.anchorMin = anchorMin;
rectTransform.anchorMax = anchorMax;
rectTransform.offsetMin = Vector2.zero;
rectTransform.offsetMax = Vector2.zero;
rectTransform.anchoredPosition = Vector2.zero;
rectTransform.localScale = Vector3.one;
}
private string FormatPrice(int requiredScore)
{
if (string.IsNullOrWhiteSpace(boxPriceFormat))
{
return requiredScore.ToString();
}
try
{
return string.Format(boxPriceFormat, requiredScore);
}
catch
{
return requiredScore.ToString();
}
}
}
}