first commit

This commit is contained in:
2026-06-04 22:14:21 +03:00
commit 677f9b8090
139 changed files with 15981 additions and 0 deletions
+213
View File
@@ -0,0 +1,213 @@
using System;
using System.Collections.Generic;
using UnityEngine;
public class PlayerStats : MonoBehaviour
{
[Header("Base Values")]
[SerializeField] private float baseMaxHealth = 100f;
[SerializeField] private float baseMaxMana = 100f;
[SerializeField] private float baseDamage = 10f;
[SerializeField] private float baseMoveSpeed = 5f;
[SerializeField] private float baseAbilityCooldownReduction = 0f;
[SerializeField] private float baseCriticalChance = 0f;
[Header("Runtime Modifiers")]
[SerializeField] private List<StatModifier> activeModifiers = new List<StatModifier>();
private readonly Dictionary<StatType, float> baseValues = new Dictionary<StatType, float>();
private readonly Dictionary<StatType, float> currentValues = new Dictionary<StatType, float>();
public event Action OnStatsChanged;
public float MaxHealth { get { return GetStat(StatType.MaxHealth); } }
public float MaxMana { get { return GetStat(StatType.MaxMana); } }
public float Damage { get { return GetStat(StatType.Damage); } }
public float MoveSpeed { get { return GetStat(StatType.MoveSpeed); } }
public float AbilityCooldownReduction { get { return GetStat(StatType.AbilityCooldownReduction); } }
public float CriticalChance { get { return GetStat(StatType.CriticalChance); } }
// Compatibility-friendly alias for lab wording: player speed is driven by PlayerStats.speed.
public float speed { get { return MoveSpeed; } }
private void Awake()
{
RecalculateStats();
}
private void OnValidate()
{
ClampBaseValues();
RecalculateStats();
}
public IReadOnlyList<StatModifier> GetActiveModifiers()
{
return activeModifiers;
}
public float GetBaseStat(StatType statType)
{
EnsureBaseValues();
return baseValues.TryGetValue(statType, out float value) ? value : 0f;
}
public float GetStat(StatType statType)
{
if (currentValues.Count == 0)
{
RecalculateStats();
}
return currentValues.TryGetValue(statType, out float value) ? value : GetBaseStat(statType);
}
public void AddModifier(StatModifier modifier)
{
if (!IsValidNumber(modifier.value))
{
Debug.LogWarning($"Ignored invalid modifier value for {modifier.statType}.");
return;
}
if (string.IsNullOrWhiteSpace(modifier.sourceId))
{
modifier.sourceId = "Runtime";
}
activeModifiers.Add(modifier);
RecalculateStats();
}
public int RemoveModifiersBySource(string sourceId)
{
if (string.IsNullOrWhiteSpace(sourceId))
{
return 0;
}
int removed = activeModifiers.RemoveAll(modifier => modifier.sourceId == sourceId);
if (removed > 0)
{
RecalculateStats();
}
return removed;
}
public int RemoveModifiersBySourcePrefix(string sourcePrefix)
{
if (string.IsNullOrWhiteSpace(sourcePrefix))
{
return 0;
}
int removed = activeModifiers.RemoveAll(modifier =>
!string.IsNullOrEmpty(modifier.sourceId) &&
modifier.sourceId.StartsWith(sourcePrefix, StringComparison.Ordinal));
if (removed > 0)
{
RecalculateStats();
}
return removed;
}
public void ClearModifiers()
{
if (activeModifiers.Count == 0)
{
return;
}
activeModifiers.Clear();
RecalculateStats();
}
public void RecalculateStats()
{
ClampBaseValues();
EnsureBaseValues();
currentValues.Clear();
foreach (StatType statType in Enum.GetValues(typeof(StatType)))
{
float baseValue = GetBaseStat(statType);
float flatAdd = 0f;
float percentAdd = 0f;
float percentMultiply = 1f;
for (int i = 0; i < activeModifiers.Count; i++)
{
StatModifier modifier = activeModifiers[i];
if (modifier.statType != statType || !IsValidNumber(modifier.value))
{
continue;
}
switch (modifier.modifierType)
{
case ModifierType.FlatAdd:
flatAdd += modifier.value;
break;
case ModifierType.PercentAdd:
percentAdd += modifier.value;
break;
case ModifierType.PercentMultiply:
percentMultiply *= Mathf.Max(0f, 1f + modifier.value);
break;
}
}
float finalValue = (baseValue + flatAdd) * Mathf.Max(0f, 1f + percentAdd) * percentMultiply;
currentValues[statType] = ClampFinalValue(statType, finalValue);
}
OnStatsChanged?.Invoke();
}
private void EnsureBaseValues()
{
baseValues[StatType.MaxHealth] = Mathf.Max(0f, baseMaxHealth);
baseValues[StatType.MaxMana] = Mathf.Max(0f, baseMaxMana);
baseValues[StatType.Damage] = Mathf.Max(0f, baseDamage);
baseValues[StatType.MoveSpeed] = Mathf.Max(0.1f, baseMoveSpeed);
baseValues[StatType.AbilityCooldownReduction] = Mathf.Clamp(baseAbilityCooldownReduction, 0f, 100f);
baseValues[StatType.CriticalChance] = Mathf.Clamp(baseCriticalChance, 0f, 100f);
}
private void ClampBaseValues()
{
baseMaxHealth = Mathf.Max(0f, baseMaxHealth);
baseMaxMana = Mathf.Max(0f, baseMaxMana);
baseDamage = Mathf.Max(0f, baseDamage);
baseMoveSpeed = Mathf.Max(0.1f, baseMoveSpeed);
baseAbilityCooldownReduction = Mathf.Clamp(baseAbilityCooldownReduction, 0f, 100f);
baseCriticalChance = Mathf.Clamp(baseCriticalChance, 0f, 100f);
}
private float ClampFinalValue(StatType statType, float value)
{
if (!IsValidNumber(value))
{
return GetBaseStat(statType);
}
switch (statType)
{
case StatType.MoveSpeed:
return Mathf.Max(0.1f, value);
case StatType.AbilityCooldownReduction:
case StatType.CriticalChance:
return Mathf.Clamp(value, 0f, 100f);
default:
return Mathf.Max(0f, value);
}
}
private bool IsValidNumber(float value)
{
return !float.IsNaN(value) && !float.IsInfinity(value);
}
}