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
194 lines
7.5 KiB
C#
194 lines
7.5 KiB
C#
using System.Collections.Generic;
|
||
using System.IO;
|
||
using UnityEngine;
|
||
using RR.Common.Utility;
|
||
|
||
namespace RR.Common.Prizes
|
||
{
|
||
/// <summary>
|
||
/// Хранилище призов за уровни. Загружает и сохраняет конфигурацию в JSON-файл.
|
||
/// Если файла нет — создаёт новый с пустым списком (нет призов ни за один уровень).
|
||
/// </summary>
|
||
public class PrizeDatabase
|
||
{
|
||
private const string FileName = "prizes.json";
|
||
|
||
private readonly Dictionary<string, LevelPrizeConfig> _configs = new();
|
||
private PrizeStandSettings _prizeStandSettings = new();
|
||
private List<QRCodeInfo> _qrCodes = new();
|
||
private bool _hasStoredQRCodes;
|
||
private string _filePath;
|
||
|
||
/// <summary>Путь к JSON-файлу.</summary>
|
||
public string FilePath => _filePath;
|
||
|
||
/// <summary>Глобальные настройки витрины.</summary>
|
||
public PrizeStandSettings PrizeStandSettings => _prizeStandSettings;
|
||
public bool HasStoredQRCodes => _hasStoredQRCodes;
|
||
|
||
// ────────────── Загрузка / сохранение ──────────────
|
||
|
||
/// <summary>
|
||
/// Загружает конфигурацию из JSON-файла рядом с исполняемым файлом.
|
||
/// Если файл отсутствует — создаёт пустой.
|
||
/// </summary>
|
||
public void Load()
|
||
{
|
||
string root = Directory.GetParent(Application.dataPath)?.ToString()
|
||
?? Application.dataPath;
|
||
_filePath = Path.Combine(root, FileName);
|
||
|
||
_configs.Clear();
|
||
_prizeStandSettings = new PrizeStandSettings();
|
||
_qrCodes = new List<QRCodeInfo>();
|
||
_hasStoredQRCodes = false;
|
||
|
||
if (File.Exists(_filePath))
|
||
{
|
||
string json = File.ReadAllText(_filePath);
|
||
var data = JsonUtility.FromJson<PrizesFileData>(json);
|
||
|
||
_prizeStandSettings = data?.prizeStandSettings ?? new PrizeStandSettings();
|
||
_qrCodes = data?.qrCodes != null ? new List<QRCodeInfo>(data.qrCodes) : new List<QRCodeInfo>();
|
||
_hasStoredQRCodes = data?.qrCodes != null;
|
||
NormalizePrizeStandSettings();
|
||
|
||
if (data?.levels != null)
|
||
{
|
||
foreach (PrizeLevelEntry entry in data.levels)
|
||
{
|
||
if (!string.IsNullOrEmpty(entry.level))
|
||
_configs[entry.level] = entry.config ?? new LevelPrizeConfig();
|
||
}
|
||
}
|
||
|
||
Debug.Log($"[PrizeDatabase] Загружено {_configs.Count} записей из {_filePath}");
|
||
}
|
||
else
|
||
{
|
||
Save();
|
||
_hasStoredQRCodes = false;
|
||
Debug.Log($"[PrizeDatabase] Файл не найден — создан пустой: {_filePath}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Сохраняет текущую конфигурацию в JSON-файл.
|
||
/// </summary>
|
||
public void Save()
|
||
{
|
||
if (string.IsNullOrEmpty(_filePath)) return;
|
||
|
||
var data = new PrizesFileData();
|
||
data.prizeStandSettings = _prizeStandSettings ?? new PrizeStandSettings();
|
||
data.qrCodes = _qrCodes != null ? new List<QRCodeInfo>(_qrCodes) : new List<QRCodeInfo>();
|
||
foreach (var kvp in _configs)
|
||
data.levels.Add(new PrizeLevelEntry { level = kvp.Key, config = kvp.Value });
|
||
|
||
string json = JsonUtility.ToJson(data, true);
|
||
File.WriteAllText(_filePath, json);
|
||
_hasStoredQRCodes = true;
|
||
|
||
Debug.Log($"[PrizeDatabase] Сохранено {_configs.Count} записей в {_filePath}");
|
||
}
|
||
|
||
// ────────────── Чтение ──────────────
|
||
|
||
/// <summary>
|
||
/// Возвращает конфигурацию приза для указанного уровня.
|
||
/// Если для уровня нет записи — возвращает <see cref="PrizeType.None"/>.
|
||
/// </summary>
|
||
public LevelPrizeConfig GetConfig(string level)
|
||
{
|
||
return _configs.TryGetValue(level, out var config) ? config : new LevelPrizeConfig();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Проверяет, есть ли запись для указанного уровня.
|
||
/// </summary>
|
||
public bool HasConfig(string level) => _configs.ContainsKey(level);
|
||
|
||
/// <summary>
|
||
/// Все записи (только для чтения).
|
||
/// </summary>
|
||
public IReadOnlyDictionary<string, LevelPrizeConfig> GetAll() => _configs;
|
||
|
||
/// <summary>
|
||
/// Возвращает глобальные настройки витрины.
|
||
/// </summary>
|
||
public PrizeStandSettings GetPrizeStandSettings()
|
||
{
|
||
NormalizePrizeStandSettings();
|
||
return _prizeStandSettings;
|
||
}
|
||
|
||
public List<QRCodeInfo> GetQRCodes()
|
||
{
|
||
return _qrCodes != null ? new List<QRCodeInfo>(_qrCodes) : new List<QRCodeInfo>();
|
||
}
|
||
|
||
// ────────────── Запись ──────────────
|
||
|
||
/// <summary>
|
||
/// Устанавливает конфигурацию приза для уровня и сохраняет файл.
|
||
/// </summary>
|
||
public void SetConfig(string level, LevelPrizeConfig config)
|
||
{
|
||
_configs[level] = config;
|
||
Save();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Удаляет запись для уровня (приз = None) и сохраняет файл.
|
||
/// </summary>
|
||
public void RemoveConfig(string level)
|
||
{
|
||
if (_configs.Remove(level))
|
||
Save();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Сохраняет глобальные настройки витрины.
|
||
/// </summary>
|
||
public void SetPrizeStandSettings(PrizeStandSettings settings)
|
||
{
|
||
_prizeStandSettings = settings ?? new PrizeStandSettings();
|
||
NormalizePrizeStandSettings();
|
||
Save();
|
||
}
|
||
|
||
public void SetQRCodes(List<QRCodeInfo> qrCodes)
|
||
{
|
||
_qrCodes = qrCodes != null ? new List<QRCodeInfo>(qrCodes) : new List<QRCodeInfo>();
|
||
Save();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Очищает все записи и сохраняет пустой файл.
|
||
/// </summary>
|
||
public void Clear()
|
||
{
|
||
_configs.Clear();
|
||
_prizeStandSettings = new PrizeStandSettings();
|
||
_qrCodes = new List<QRCodeInfo>();
|
||
Save();
|
||
}
|
||
|
||
private void NormalizePrizeStandSettings()
|
||
{
|
||
_prizeStandSettings ??= new PrizeStandSettings();
|
||
_prizeStandSettings.boxes ??= new List<PrizeStandBoxConfig>();
|
||
|
||
_prizeStandSettings.boxes.Sort((a, b) => a.boxNumber.CompareTo(b.boxNumber));
|
||
|
||
for (int i = _prizeStandSettings.boxes.Count - 1; i >= 0; i--)
|
||
{
|
||
PrizeStandBoxConfig box = _prizeStandSettings.boxes[i];
|
||
if (box == null || box.boxNumber <= 0)
|
||
_prizeStandSettings.boxes.RemoveAt(i);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|