using System;
using System.Collections.Generic;
using SepCore.Definition;
using UnityEngine;
namespace SepCore.Battle
{
///
/// 单个属性实例容器。
/// 维护基础值、修饰器列表,通过脏标记(Dirty Flag)缓存计算结果,并基于 StatConfig 进行边界钳制。
///
public sealed class CharacterStat
{
private readonly List _modifiers = new();
private float _baseValue;
private bool _isBaseValueExplicitlySet;
private bool _isDirty = true;
private float _cachedValue;
private StatConfig _config;
public StatType Type { get; }
public StatConfig Config
{
get
{
if (_config == null && GameEntry.Luban != null && GameEntry.Luban.IsReady)
{
_config = GameEntry.Luban.Get((int)Type);
if (!_isBaseValueExplicitlySet && _config != null)
{
_baseValue = _config.DefaultBaseValue;
_isDirty = true;
}
}
return _config;
}
set
{
_config = value;
if (!_isBaseValueExplicitlySet && _config != null)
{
_baseValue = _config.DefaultBaseValue;
}
_isDirty = true;
}
}
public float BaseValue
{
get
{
if (!_isBaseValueExplicitlySet)
{
if (Config != null)
{
_baseValue = Config.DefaultBaseValue;
}
else if (Type is StatType.DamagePercent or StatType.AttackSpeed)
{
_baseValue = 1.0f;
}
}
return _baseValue;
}
set
{
_isBaseValueExplicitlySet = true;
if (Math.Abs(_baseValue - value) > 1e-6f)
{
_baseValue = value;
_isDirty = true;
}
}
}
public float Value
{
get
{
if (_isDirty)
{
_cachedValue = CalculateFinalValue();
_isDirty = false;
}
return _cachedValue;
}
}
public IReadOnlyList Modifiers => _modifiers;
public CharacterStat(StatType type, float baseValue = 0f, StatConfig config = null)
{
Type = type;
_config = config;
if (Math.Abs(baseValue) > 1e-6f)
{
_baseValue = baseValue;
_isBaseValueExplicitlySet = true;
}
else if (_config != null)
{
_baseValue = _config.DefaultBaseValue;
_isBaseValueExplicitlySet = true;
}
else if (type is StatType.DamagePercent or StatType.AttackSpeed)
{
_baseValue = 1.0f;
}
_isDirty = true;
}
public void AddModifier(StatModifier modifier)
{
if (modifier == null)
{
throw new ArgumentNullException(nameof(modifier));
}
_modifiers.Add(modifier);
_isDirty = true;
}
public bool RemoveModifier(StatModifier modifier)
{
if (modifier == null)
{
return false;
}
if (_modifiers.Remove(modifier))
{
_isDirty = true;
return true;
}
return false;
}
public int RemoveAllModifiersFromSource(object source)
{
if (source == null)
{
return 0;
}
int removed = _modifiers.RemoveAll(m => ReferenceEquals(m.Source, source) || Equals(m.Source, source));
if (removed > 0)
{
_isDirty = true;
}
return removed;
}
public void ClearModifiers()
{
if (_modifiers.Count > 0)
{
_modifiers.Clear();
_isDirty = true;
}
}
public void MarkDirty()
{
_isDirty = true;
}
private float CalculateFinalValue()
{
float sumFlat = 0f;
float sumPercentAdd = 0f;
float prodPercentMult = 1f;
for (int i = 0; i < _modifiers.Count; i++)
{
StatModifier mod = _modifiers[i];
switch (mod.Type)
{
case ModifierType.Flat:
sumFlat += mod.Value;
break;
case ModifierType.PercentAdd:
sumPercentAdd += mod.Value;
break;
case ModifierType.PercentMult:
prodPercentMult *= mod.Value;
break;
}
}
float finalValue = ((BaseValue + sumFlat) * (1f + sumPercentAdd)) * prodPercentMult;
return ClampStat(finalValue);
}
private float ClampStat(float value)
{
StatConfig config = Config;
if (config != null)
{
if (config.HasMin && value < config.MinValue)
{
value = config.MinValue;
}
if (config.HasMax && value > config.MaxValue)
{
value = config.MaxValue;
}
}
return value;
}
}
}