2026-07-14 11:14:13 +03:00

445 lines
15 KiB
C#
Raw Permalink 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.Collections.Generic;
using UnityEngine;
public class Setting : IViewStringSupplier
{
#region FIELDS
//general
public string Id { get; set; }
public string Alias { get; set; }
public string Group { get; set; }
public SettingType Type { get; set; }
public bool Cycling { get; set; }
//type: Bool
public bool BoolValue { get; set; }
//type: Int
public int IntValue { get; set; }
public int IntMin { get; set; }
public int IntMax { get; set; }
public int IntStep { get; set; }
//type: Float
public float FloatValue { get; set; }
public float FloatMin { get; set; }
public float FloatMax { get; set; }
public float FloatStep { get; set; }
//type: String
public string StringValue { get; set; }
public List<string> AllowedValues { get; private set; }
#endregion
#region CONSTRUCTORS
/// <summary>
/// Базовый конструктор
/// </summary>
private Setting(string id,
string alias,
string group,
SettingType type,
bool cycling)
{
this.Id = id;
this.Alias = alias;
this.Group = group;
this.Type = type;
this.Cycling = cycling;
}
/// <summary>
/// Bool конструктор
/// </summary>
public Setting(string id, string alias, string group, SettingType type, bool value, bool cycling) : this(id, alias, group, type, cycling)
{
//Валидация типа
CheckType(type, SettingType.Bool, id);
BoolValue = value;
}
/// <summary>
/// Int конструктор
/// </summary>
public Setting(string id, string alias, string group, SettingType type, bool cycling, int value, int min, int max, int step) : this(id, alias, group, type, cycling)
{
CheckType(type, SettingType.Int, id);
ValidateInt(value: ref value,
min: ref min,
max: ref max,
step: ref step,
settingId: id);
this.IntValue = value;
this.IntMin = min;
this.IntMax = max;
this.IntStep = step;
}
/// <summary>
/// Float конструктор
/// </summary>
public Setting(string id, string alias, string group, SettingType type, bool cycling, float value, float min, float max, float step) : this(id, alias, group, type, cycling)
{
CheckType(type, SettingType.Float, id);
ValidateFloat(value: ref value,
min: ref min,
max: ref max,
step: ref step,
settingId: id);
this.FloatValue = value;
this.FloatMin = min;
this.FloatMax = max;
this.FloatStep = step;
}
/// <summary>
/// String конструктор
/// </summary>
public Setting(string id, string alias, string group, SettingType type, bool cycling, string value, List<string> allowedValues) : this(id, alias, group, type, cycling)
{
CheckType(type, SettingType.String, id);
ValidateString(ref value, allowedValues, id);
this.AllowedValues = allowedValues;
this.StringValue = value;
}
#endregion
#region HELPERS
private void CheckType(SettingType type, SettingType expectedType, string id)
{
if (type != expectedType) throw new System.Exception($"Настройка {id}. Несоответствие типов.");
}
private void ValidateInt(ref int value, ref int min, ref int max, ref int step, string settingId)
{
bool hasWarning = false;
string WarningText = string.Empty;
if (min > max)
{
hasWarning = true;
WarningText += $"Максимум ({max}) ниже минимума ({min}). Минимум и максимум поменяны местами.\n";
(min, max) = (max, min);
}
if (value < min)
{
hasWarning = true;
WarningText += $"Значение {value} ниже минимума {min}. Установлено значение {min}.\n";
value = min;
}
if (value > max)
{
hasWarning = true;
WarningText += $"Значение {value} выше максимума {max}. Установлено значение {max}.\n";
value = max;
}
int delta = max - min;
if (step > delta)
{
hasWarning = true;
WarningText += $"Шаг ({step}) установлен выше чем разница максимума и минимума ({delta}). Шаг установлен в значение {delta}.\n";
step = delta;
}
if (step < 0)
{
hasWarning = true;
WarningText += $"Шаг ({step}) не может быть отрицательным. Значение инвертировано ({-step}).\n";
step = -step;
}
if (hasWarning) Debug.LogWarning($"Настройка {settingId}:\n" + WarningText);
}
private void ValidateFloat(ref float value, ref float min, ref float max, ref float step, string settingId)
{
bool hasWarning = false;
string WarningText = string.Empty;
if (min > max)
{
hasWarning = true;
WarningText += $"Максимум ({max}) ниже минимума ({min}). Минимум и максимум поменяны местами.\n";
(min, max) = (max, min);
}
if (value < min)
{
hasWarning = true;
WarningText += $"Значение {value} ниже минимума {min}. Установлено значение {min}.\n";
value = min;
}
if (value > max)
{
hasWarning = true;
WarningText += $"Значение {value} выше максимума {max}. Установлено значение {max}.\n";
value = max;
}
float delta = max - min;
if (step > delta)
{
hasWarning = true;
WarningText += $"Шаг ({step}) установлен выше чем разница максимума и минимума ({delta}). Шаг установлен в значение {delta}.\n";
step = delta;
}
if (step < 0)
{
hasWarning = true;
WarningText += $"Шаг ({step}) не может быть отрицательным. Значение инвертировано ({-step}).\n";
step = -step;
}
if (hasWarning) Debug.LogWarning($"Настройка {settingId}:\n" + WarningText);
}
private void ValidateString(ref string value, List<string> allowedValues, string settingId)
{
if (allowedValues == null)
{
Debug.LogWarning($"Настройка {settingId}:\nСписок значений null");
}
else if (allowedValues.Count == 0)
{
Debug.LogWarning($"Настройка {settingId}:\nСписок значений пуст");
}
else if (!allowedValues.Contains(value))
{
string newValue = allowedValues[allowedValues.Count / 2];
Debug.LogWarning($"Настройка {settingId}:\nСписок значений не содержит установленного значения{value}. Установлено среднее значение {newValue}.");
value = newValue;
}
}
#endregion
#region CONTROLS
//Возвращает изменилось ли значение
public bool ChangeValue(int direction)
=> Type switch
{
SettingType.Bool => ChangeBool(direction),
SettingType.Int => ChangeInt(direction),
SettingType.Float => ChangeFloat(direction),
SettingType.String => ChangeString(direction),
_ => false
};
private bool ChangeBool(int direction)
{
if (direction == 0) return false;
if (Cycling)
{
BoolValue = !BoolValue;
return true;
}
else
{
bool newValue = direction > 0;
if (newValue == BoolValue) return false;
BoolValue = newValue;
return true;
}
}
private bool ChangeInt(int direction)
{
if (direction == 0) return false;
int sign = Mathf.Clamp(direction, -1, 1);
int newValue = IntValue + IntStep * sign;
if (Cycling)
{
int range = IntMax - IntMin + 1;
if (newValue > IntMax)
newValue = IntMin + (newValue - IntMax - 1) % range;
else if (newValue < IntMin)
newValue = IntMax - (IntMin - newValue - 1) % range;
}
else
{
newValue = Mathf.Clamp(newValue, IntMin, IntMax);
}
if (IntValue == newValue) return false;
IntValue = newValue;
return true;
}
private bool ChangeFloat(int direction)
{
if (direction == 0) return false;
float sign = Mathf.Sign(direction);
float newValue = FloatValue + FloatStep * sign;
if (Cycling)
{
float range = FloatMax - FloatMin;
if (range <= 0)
{
// Если диапазон нулевой, просто возвращаем минимальное значение
newValue = FloatMin;
}
else if (newValue > FloatMax)
{
// Для циклического перехода через максимум
float overflow = newValue - FloatMax;
newValue = FloatMin + overflow % range;
}
else if (newValue < FloatMin)
{
// Для циклического перехода через минимум
float underflow = FloatMin - newValue;
newValue = FloatMax - underflow % range;
}
}
else
{
newValue = Mathf.Clamp(newValue, FloatMin, FloatMax);
}
// Сравнение float с учетом погрешности
if (Mathf.Abs(FloatValue - newValue) < float.Epsilon)
return false;
FloatValue = newValue;
return true;
}
private bool ChangeString(int direction)
{
if (direction == 0 || AllowedValues == null || AllowedValues.Count == 0)
return false;
int sign = direction > 0 ? 1 : -1;
int currentIndex = AllowedValues.IndexOf(StringValue);
// Если значение не в списке
if (currentIndex < 0)
{
return false;
}
int newIndex = currentIndex + sign;
int maxIndex = AllowedValues.Count - 1;
if (Cycling)
{
// Циклический переход
if (newIndex > maxIndex)
newIndex = 0;
else if (newIndex < 0)
newIndex = maxIndex;
}
else
{
// Без циклического режима - ограничение границами
newIndex = Mathf.Clamp(newIndex, 0, maxIndex);
}
if (currentIndex == newIndex)
return false;
StringValue = AllowedValues[newIndex];
return true;
}
#endregion
#region SERIALIZATION
//Преобразовать класс в DTO
public SettingData ToSerializableData()
{
return new SettingData
{
Id = this.Id,
Alias = this.Alias,
Group = this.Group,
Type = this.Type,
Cycling = this.Cycling,
BoolValue = this.BoolValue,
IntValue = this.IntValue,
FloatValue = this.FloatValue,
StringValue = this.StringValue,
IntMin = this.IntMin,
IntMax = this.IntMax,
IntStep = this.IntStep,
FloatMin = this.FloatMin,
FloatMax = this.FloatMax,
FloatStep = this.FloatStep,
AllowedValues = this.AllowedValues
};
}
//Восстановить класс из DTO
public static Setting FromSerializableData(SettingData data)
{
return data.Type switch
{
SettingType.Bool => new Setting(id: data.Id,
alias: data.Alias,
group: data.Group,
type: data.Type,
cycling:data.Cycling,
value: data.BoolValue),
SettingType.Int => new Setting(id: data.Id,
alias: data.Alias,
group: data.Group,
type: data.Type,
cycling: data.Cycling,
value: data.IntValue,
min: data.IntMin,
max: data.IntMax,
step: data.IntStep),
SettingType.Float=> new Setting(id: data.Id,
alias: data.Alias,
group: data.Group,
type: data.Type,
cycling: data.Cycling,
value: data.FloatValue,
min: data.FloatMin,
max: data.FloatMax,
step: data.FloatStep),
SettingType.String=>new Setting(id: data.Id,
alias: data.Alias,
group: data.Group,
type: data.Type,
cycling: data.Cycling,
value: data.StringValue,
allowedValues: data.AllowedValues),
_ => null
};
}
public string GetStringForView()
{
return Type switch
{
SettingType.Bool => BoolValue.ToString(),
SettingType.Int => IntValue.ToString(),
SettingType.Float => FloatValue.ToString(),
SettingType.String => StringValue,
_ => null
};
}
#endregion
}