Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
- завершен спринт 24 марта - 2 апреля с опережением плана и закрытием задач раньше срока - расширена и стабилизирована система призов: добавлены и доработаны QR-tier конфиги, новые типы и модели данных, сохранение `qrCodes` в `prizes.json`, fallback-логика и синхронизация через `PrizeSystemManager`, `PrizeDatabase` и `LevelPrizeConfig` - переработан flow настройки QR-призов: добавлены `QRCodeSettingsModal` и связанный UI-код, разнесены короткий текст приза и полный текст сообщения, добавлены шаблоны форматирования, корректная подстановка текста в QR и в UI-модалках, а также сохранение конфигурации между запусками - доработан flow суперприза и текстовых настроек: добавлены fallback-правила показа, корректная обработка пустых строк и строк из одних пробелов, обновлены binders и показ текста в меню - реализована и обновлена отдельная настройка витрины: добавлена `PrizeStandSettingsModal`, обновлена навигация строк, логика выбора ячейки и порогов очков, циклический выбор ячеек, ускорение изменения значений и исправления для keyboard/gamepad UX - доработана игровая модалка витрины `PrizeStandModal`: добавлен вывод очков игрока, форматируемый текст счета, улучшено отображение ячеек и связанная логика выбора приза - исправлен подсчет очков и интеграция с prize flow: `ScoreSystem` переведен на событийное обновление текущего счета, добавлены `CurrentScore`, `CurrentScoreChanged` и корректный display score, витрина и связанные UI-элементы теперь получают актуальные очки, а не нулевые или несвоевременные preview-значения - переработаны модальные окна и навигация UI: улучшены `Modal`, `ModalManager`, `UIRowPointer`, `RulesModal`, `QRCodeModal`, `PrizeStandModal`, `LevelPrizeSettingsModal` и др., чтобы фокус, указатель и submit работали консистентно без мыши - обновлен win/prize UI: доработаны `WinBannerController`, `GameEndUIController`, `MainMenu`, `QRCodeUIController`, `TopPanelTextController` и настройки UI, улучшены тексты баннеров, отображение призов и итогового счета - обновлены сцены и ассеты под новый flow: `MainMenu`, `Level UI`, prefab'ы строк настроек и кнопок, глобальные префабы, шрифты TMP, иконки, recovery-данные и ресурсы UI - добавлены вспомогательные UI-компоненты и служебные скрипты: `ButtonTextFocusColor`, `SelectableDescriptionTarget`, `QRTier` и другие изменения, поддерживающие новый конфигурируемый prize UI
174 lines
6.4 KiB
C#
174 lines
6.4 KiB
C#
using UnityEngine;
|
||
using RR.Common.Managers;
|
||
using RR.Common.Prizes;
|
||
using RR.Game2D.Core.Managers;
|
||
|
||
namespace RR.Game2D.Behaviour
|
||
{
|
||
/// <summary>
|
||
/// Компонент финиша уровня с возможностью выдачи приза.
|
||
/// Устанавливается на объект с триггером и тегом "Finish".
|
||
/// </summary>
|
||
[RequireComponent(typeof(Collider2D))]
|
||
public class LevelFinish : MonoBehaviour
|
||
{
|
||
[Header("Настройки приза")]
|
||
[Tooltip("Включить выдачу приза при завершении уровня")]
|
||
[SerializeField] private bool enablePrizeOnFinish = true;
|
||
|
||
[Tooltip("Задержка перед выдачей приза (в секундах)")]
|
||
[SerializeField] private float prizeDelay = 0.5f;
|
||
|
||
[Header("Отладка")]
|
||
[SerializeField] private bool debugMode;
|
||
|
||
private bool _prizeGiven;
|
||
private bool _waitingForPrize;
|
||
|
||
private void OnTriggerEnter2D(Collider2D collision)
|
||
{
|
||
// Проверяем, что это игрок
|
||
if (!collision.CompareTag("Player")) return;
|
||
|
||
if (GameManager.Instance != null && GameManager.Instance.IsCurrentLevelFinal())
|
||
{
|
||
GameManager.Instance.SetPendingWinGameTime(GameManager.Instance.GetCurrentGameTime());
|
||
}
|
||
|
||
if (enablePrizeOnFinish && !_prizeGiven && HasPrizeForCurrentLevel())
|
||
{
|
||
// Есть приз — выдаём и ждём результата, потом переходим
|
||
TryGivePrize();
|
||
}
|
||
else
|
||
{
|
||
// Приза нет или он отключён — переходим сразу
|
||
GameManager.Instance.NextLevel();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Проверяет, назначен ли приз для текущего уровня.
|
||
/// </summary>
|
||
private bool HasPrizeForCurrentLevel()
|
||
{
|
||
var psm = PrizeSystemManager.Instance;
|
||
if (psm == null || psm.Database == null) return false;
|
||
|
||
string level = $"{GameManager.Instance.World}-{GameManager.Instance.Stage}";
|
||
LevelPrizeConfig config = psm.Database.GetConfig(level);
|
||
return config.type != PrizeType.None;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Пытается выдать приз игроку, если выполнены все условия.
|
||
/// </summary>
|
||
private void TryGivePrize()
|
||
{
|
||
// Проверка, что приз еще не был выдан
|
||
if (_prizeGiven)
|
||
{
|
||
if (debugMode)
|
||
Debug.Log("[LevelFinish] Приз уже был выдан на этом финише.");
|
||
return;
|
||
}
|
||
|
||
// Проверка, включена ли выдача призов в этом компоненте
|
||
if (!enablePrizeOnFinish)
|
||
{
|
||
if (debugMode)
|
||
Debug.Log("[LevelFinish] Выдача призов отключена в компоненте.");
|
||
return;
|
||
}
|
||
|
||
_prizeGiven = true;
|
||
_waitingForPrize = true;
|
||
|
||
// Подписываемся на результат выдачи приза
|
||
var psm = PrizeSystemManager.Instance;
|
||
if (psm != null)
|
||
psm.OnAnyPrizeResult += OnPrizeFinished;
|
||
|
||
if (prizeDelay > 0)
|
||
{
|
||
Invoke(nameof(GivePrize), prizeDelay);
|
||
}
|
||
else
|
||
{
|
||
GivePrize();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Вызывается после завершения выдачи приза (успешно или нет).
|
||
/// </summary>
|
||
private void OnPrizeFinished(PrizeType type, bool success)
|
||
{
|
||
if (!_waitingForPrize) return;
|
||
_waitingForPrize = false;
|
||
|
||
var psm = PrizeSystemManager.Instance;
|
||
if (psm != null)
|
||
psm.OnAnyPrizeResult -= OnPrizeFinished;
|
||
|
||
if (debugMode)
|
||
Debug.Log($"[LevelFinish] Приз «{type}» завершён (успех: {success}). Переход на следующий уровень.");
|
||
|
||
GameManager.Instance.NextLevel();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Выдает приз через PrizeSystemManager — тип приза берётся из JSON-базы для текущего уровня.
|
||
/// </summary>
|
||
private void GivePrize()
|
||
{
|
||
if (debugMode)
|
||
Debug.Log("[LevelFinish] Выдаю приз игроку!");
|
||
|
||
if (PrizeSystemManager.Instance != null)
|
||
PrizeSystemManager.Instance.GivePrizeForCurrentLevel();
|
||
else
|
||
{
|
||
if (debugMode)
|
||
Debug.LogWarning("[LevelFinish] PrizeSystemManager не найден.");
|
||
// Если менеджер не найден — переходим сразу
|
||
_waitingForPrize = false;
|
||
GameManager.Instance.NextLevel();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Сбрасывает состояние выдачи приза (для повторного использования).
|
||
/// </summary>
|
||
public void ResetPrize()
|
||
{
|
||
_prizeGiven = false;
|
||
_waitingForPrize = false;
|
||
}
|
||
|
||
private void OnDestroy()
|
||
{
|
||
if (_waitingForPrize)
|
||
{
|
||
var psm = PrizeSystemManager.Instance;
|
||
if (psm != null)
|
||
psm.OnAnyPrizeResult -= OnPrizeFinished;
|
||
}
|
||
}
|
||
|
||
#if UNITY_EDITOR
|
||
private void OnDrawGizmos()
|
||
{
|
||
// Визуализация зоны финиша в редакторе
|
||
Collider2D col = GetComponent<Collider2D>();
|
||
if (col != null)
|
||
{
|
||
Gizmos.color = enablePrizeOnFinish ? Color.green : Color.yellow;
|
||
Gizmos.DrawWireCube(col.bounds.center, col.bounds.size);
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
}
|
||
|