Danro/Assets/Scripts/Settings/Core/SettingsDatabase.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

252 lines
9.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using UnityEngine;
namespace RR.Common.Settings
{
public enum SettingValueType
{
Bool,
Int,
Float,
String,
Button, // Кнопка - не хранит значение, только вызывает действие
OptionPicker // Выбор из списка опций (enum-like)
}
public enum ApplyTarget
{
None,
Audio // Применить к звуку через AudioManager (музыка или эффекты)
}
public enum AudioTargetType
{
Music, // Музыка
Sfx // Звуковые эффекты
}
public enum IntDisplayFormat
{
Slider, // Обычный слайдер
ValueSelector // Выбор значения (со стрелками или дропдауном)
}
public enum FloatDisplayFormat
{
Slider, // Обычный слайдер
ValueSelector // Выбор значения (со стрелками)
}
public enum OptionPickerDisplayFormat
{
Default, // Текущее поведение: dropdown или отдельный option picker prefab
ValueSelector // Выбор значения через тот же prefab, что и для Int/Float
}
[Serializable]
public struct SettingValue
{
public SettingValueType type;
public bool b;
public int i;
public float f;
public string s;
public static SettingValue Bool(bool v) => new() { type = SettingValueType.Bool, b = v };
public static SettingValue Int(int v) => new() { type = SettingValueType.Int, i = v };
public static SettingValue Float(float v) => new() { type = SettingValueType.Float, f = v };
public static SettingValue String(string v) => new() { type = SettingValueType.String, s = v };
public static SettingValue OptionPicker(int v) => new() { type = SettingValueType.OptionPicker, i = v };
/// <summary>
/// Создает компактную версию для сериализации (только нужные поля)
/// </summary>
public SettingValueCompact ToCompact()
{
return new SettingValueCompact
{
type = (int)type,
value = type switch
{
SettingValueType.Bool => b ? "1" : "0",
SettingValueType.Int => i.ToString(),
SettingValueType.OptionPicker => i.ToString(),
SettingValueType.Float => f.ToString("F6"),
SettingValueType.String => s ?? "",
_ => ""
}
};
}
/// <summary>
/// Создает SettingValue из компактной версии
/// </summary>
public static SettingValue FromCompact(SettingValueCompact compact)
{
var type = (SettingValueType)compact.type;
return type switch
{
SettingValueType.Bool => Bool(compact.value == "1"),
SettingValueType.Int => Int(int.TryParse(compact.value, out int i) ? i : 0),
SettingValueType.OptionPicker => OptionPicker(int.TryParse(compact.value, out int optionIndex) ? optionIndex : 0),
SettingValueType.Float => Float(float.TryParse(compact.value, out float f) ? f : 0f),
SettingValueType.String => String(compact.value ?? ""),
_ => default
};
}
}
/// <summary>
/// Компактная версия SettingValue для сериализации (экономит место в JSON)
/// </summary>
[Serializable]
public struct SettingValueCompact
{
public int type;
public string value;
}
[Serializable]
public class SettingEntry
{
public string key;
public string displayName;
[TextArea] public string description;
public string group = "General";
[Tooltip("Показывать ли настройку в UI меню настроек")]
public bool shownInSettings = true;
[Tooltip("Доступна ли настройка для изменения извне (через API/файл)")]
public bool exposed = false;
public SettingValueType valueType;
// Defaults
public bool defaultBool = true;
public int defaultInt = 0;
public float defaultFloat = 1f;
public string defaultString = "";
// Ranges (used for Int/Float)
public int minInt = 0;
public int maxInt = 10;
[Tooltip("Формат отображения для Int (слайдер или выбор значения)")]
public IntDisplayFormat intDisplayFormat = IntDisplayFormat.Slider;
[Tooltip("Шаг для выбора значения Int (используется только для ValueSelector)")]
public int intStep = 1;
public float minFloat = 0f;
public float maxFloat = 1f;
[Tooltip("Отображать float значение как проценты (0-100%)")]
public bool displayAsPercentage = false;
[Tooltip("Формат отображения для Float (слайдер или выбор значения)")]
public FloatDisplayFormat floatDisplayFormat = FloatDisplayFormat.Slider;
[Tooltip("Шаг для выбора значения Float (используется только для ValueSelector)")]
public float floatStep = 0.1f;
public ApplyTarget applyTarget = ApplyTarget.None;
// Audio params (используется только для ApplyTarget.Audio)
[Tooltip("Тип звука: музыка или звуковые эффекты")]
public AudioTargetType audioTargetType = AudioTargetType.Music;
// Button params (для типа Button)
[Tooltip("Текст на кнопке (если не задан displayName)")]
public string buttonText;
[Tooltip("Дополнительные данные для кнопки (URL, имя сцены и т.д.)")]
public string buttonData;
[Tooltip("Цвет кнопки")]
public Color buttonColor = Color.white;
[Tooltip("Текстура/спрайт кнопки")]
public Sprite buttonSprite;
// OptionPicker params (для типа OptionPicker)
[Tooltip("Список вариантов выбора")]
public List<string> options;
[Tooltip("Индекс варианта по умолчанию")]
public int defaultOptionIndex;
[Tooltip("Формат отображения для Option Picker")]
public OptionPickerDisplayFormat optionPickerDisplayFormat = OptionPickerDisplayFormat.Default;
public SettingValue DefaultValue => valueType switch
{
SettingValueType.Bool => SettingValue.Bool(defaultBool),
SettingValueType.Int => SettingValue.Int(defaultInt),
SettingValueType.Float => SettingValue.Float(defaultFloat),
SettingValueType.String => SettingValue.String(defaultString ?? ""),
SettingValueType.Button => default, // Кнопки не хранят значение
SettingValueType.OptionPicker => SettingValue.OptionPicker(options != null && options.Count > 0
? Mathf.Clamp(defaultOptionIndex, 0, options.Count - 1)
: 0),
_ => default
};
public SettingValue Sanitize(SettingValue v)
{
if (valueType == SettingValueType.OptionPicker)
{
if (v.type != SettingValueType.OptionPicker && v.type != SettingValueType.Int)
return DefaultValue;
}
else if (v.type != valueType)
{
return DefaultValue;
}
// защита от кривых min/max
int iMin = Mathf.Min(minInt, maxInt);
int iMax = Mathf.Max(minInt, maxInt);
float fMin = Mathf.Min(minFloat, maxFloat);
float fMax = Mathf.Max(minFloat, maxFloat);
return valueType switch
{
SettingValueType.Bool => SettingValue.Bool(v.b),
SettingValueType.Int => SettingValue.Int(Mathf.Clamp(v.i, iMin, iMax)),
SettingValueType.Float => SettingValue.Float(Mathf.Clamp(v.f, fMin, fMax)),
SettingValueType.String => SettingValue.String(v.s ?? ""),
SettingValueType.Button => default,
SettingValueType.OptionPicker => SettingValue.OptionPicker(options != null && options.Count > 0
? Mathf.Clamp(v.i, 0, options.Count - 1)
: 0),
_ => DefaultValue
};
}
}
[CreateAssetMenu(menuName = "Settings/Database")]
public class SettingsDatabase : ScriptableObject
{
public List<SettingEntry> entries = new();
public SettingEntry Find(string key)
=> entries.Find(e => string.Equals(e.key, key, StringComparison.Ordinal));
private void OnValidate()
{
var set = new HashSet<string>();
foreach (var e in entries)
{
if (e == null) continue;
if (string.IsNullOrWhiteSpace(e.key))
Debug.LogWarning($"[SettingsDatabase] Empty key in {name}", this);
else if (!set.Add(e.key))
Debug.LogWarning($"[SettingsDatabase] Duplicate key '{e.key}' in {name}", this);
}
}
}
}