2025-09-27 18:19:56 +01:00

273 lines
11 KiB
C#

using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Events;
using Kryz.CharacterStats;
namespace Kryz.CharacterStats.Examples
{
[System.Serializable]
public class StatOverride
{
[SerializeField] private StatDefinition statKey;
[SerializeField] private float baseValue;
public string StatKey => statKey.StatKey;
public float BaseValue => baseValue;
}
public class CharacterStats : MonoBehaviour
{
[Header("Stat Overrides")]
[SerializeField] private List<StatOverride> statOverrides = new List<StatOverride>();
[Header("Runtime Stats (Auto-Generated)")]
[SerializeField] private bool showDebugStats = false;
// Main stats dictionary - all stats live here
private Dictionary<string, CharacterStat> allStats = new Dictionary<string, CharacterStat>();
// Compatibility dictionaries for existing systems
public Dictionary<string, CharacterStat> primaryStatsDictionary = new Dictionary<string, CharacterStat>();
public Dictionary<string, CharacterStat> secondaryStatsDictionary = new Dictionary<string, CharacterStat>();
// Events
public UnityEvent onUpdateStatValues = new UnityEvent();
public UnityEvent onAllStatsUpdated = new UnityEvent();
// Quick access properties for primary stats (for compatibility)
public CharacterStat Cunning => GetStat("cunning");
public CharacterStat Flow => GetStat("flow");
public CharacterStat Presence => GetStat("presence");
protected virtual void Awake()
{
InitializeStatsFromRegistry();
InitializeBaseStatOverrides();
onUpdateStatValues.AddListener(UpdateSecondaryStatsBasedOnPrimaryStats);
}
private void InitializeStatsFromRegistry()
{
// Clear existing stats
allStats.Clear();
primaryStatsDictionary.Clear();
secondaryStatsDictionary.Clear();
// Get all stat definitions from registry
var statDefinitions = StatRegistry.Instance.GetAllStats();
if (statDefinitions.Length == 0)
{
Debug.LogWarning($"CharacterStats on {gameObject.name}: No stat definitions found in StatRegistry!");
return;
}
// Create CharacterStat for each definition
foreach (var statDef in statDefinitions)
{
var characterStat = new CharacterStat(statDef.DefaultBaseValue);
characterStat.statDefinition = statDef; // Store reference to definition
allStats[statDef.StatKey] = characterStat;
// Populate compatibility dictionaries
string lowerKey = statDef.StatKey.ToLower();
if (statDef.IsPrimary)
{
primaryStatsDictionary[lowerKey] = characterStat;
}
else
{
secondaryStatsDictionary[lowerKey] = characterStat;
}
if (showDebugStats)
{
Debug.Log($"Initialized stat: {statDef.StatKey} (Base: {statDef.DefaultBaseValue}, Category: {statDef.Category})");
}
}
Debug.Log($"CharacterStats on {gameObject.name}: Initialized {allStats.Count} stats");
}
private void InitializeBaseStatOverrides()
{
for (int i = 0; i < statOverrides.Count; i++)
{
allStats[statOverrides[i].StatKey].BaseValue = statOverrides[i].BaseValue;
}
}
// Main stat access method
public CharacterStat GetStat(string statKey)
{
if (allStats.TryGetValue(statKey, out CharacterStat stat))
{
return stat;
}
Debug.LogWarning($"CharacterStats: Stat '{statKey}' not found!");
return null;
}
// Safe stat access that won't return null
public CharacterStat GetStatSafe(string statKey)
{
var stat = GetStat(statKey);
if (stat == null)
{
// Return a dummy stat to prevent null reference exceptions
return new CharacterStat(0f);
}
return stat;
}
// Get stats by category
public CharacterStat[] GetStatsByCategory(StatCategory category)
{
return allStats.Values
.Where(stat => stat.statDefinition.Category == category)
.ToArray();
}
// Get all primary stats
public CharacterStat[] GetPrimaryStats()
{
return allStats.Values
.Where(stat => stat.statDefinition.IsPrimary)
.ToArray();
}
public CharacterStat[] GetAllStats()
{
return allStats.Values.ToArray();
}
// Get all UI-visible stats
public CharacterStat[] GetUIVisibleStats()
{
return allStats.Values
.Where(stat => stat.statDefinition.ShowInUI)
.ToArray();
}
public void IncreaseAllStatPoints(int amount)
{
onUpdateStatValues.Invoke();
}
public void UpdateSecondaryStatsBasedOnPrimaryStats()
{
// Get primary stats
var cunning = GetStat("cunning");
var flow = GetStat("flow");
var presence = GetStat("presence");
if (cunning == null || flow == null || presence == null)
{
Debug.LogError("CharacterStats: Primary stats not found! Make sure cunning, flow, and presence StatDefinitions exist.");
return;
}
// Remove previous modifiers from all affected stats
RemovePrimaryStatInfluences();
// Apply Cunning influences
ApplyStatInfluence("critchance", cunning.Value * GameConstants.CharacterStatsBalancing.CritChanceIncreasePerCunning, GameConstants.ObjectSources.CunningSource);
ApplyStatInfluence("critdamage", cunning.Value * GameConstants.CharacterStatsBalancing.CritDamageIncreasePerCunning, GameConstants.ObjectSources.CunningSource);
ApplyStatInfluence("movementspeed", cunning.Value * GameConstants.CharacterStatsBalancing.MovementSpeedIncreasePerCunning, GameConstants.ObjectSources.CunningSource);
// Apply Flow influences
ApplyStatInfluence("maxmana", flow.Value * GameConstants.CharacterStatsBalancing.MaxManaIncreasePerFlow, GameConstants.ObjectSources.FlowSource);
ApplyStatInfluence("manaregen", flow.Value * GameConstants.CharacterStatsBalancing.ManaRegenIncreasePerFlow, GameConstants.ObjectSources.FlowSource);
ApplyStatInfluence("cooldownreduction", flow.Value * GameConstants.CharacterStatsBalancing.CooldownReductionIncreasePerFlow, GameConstants.ObjectSources.FlowSource);
ApplyStatInfluence("attackspeed", flow.Value * GameConstants.CharacterStatsBalancing.AttackSpeedIncreasePerFlow, GameConstants.ObjectSources.FlowSource);
// Apply Presence influences
ApplyStatInfluence("areaeffectiveness", presence.Value * GameConstants.CharacterStatsBalancing.AreaEffectivenessIncreasePerPresence, GameConstants.ObjectSources.PresenceSource);
ApplyStatInfluence("reputationgainincrease", presence.Value * GameConstants.CharacterStatsBalancing.ReputationGainIncreasePerPresence, GameConstants.ObjectSources.PresenceSource);
ApplyStatInfluence("goldcostreduction", presence.Value * GameConstants.CharacterStatsBalancing.GoldCostReductionPerPresence, GameConstants.ObjectSources.PresenceSource);
ApplyStatInfluence("aurapower", presence.Value * GameConstants.CharacterStatsBalancing.AuraPowerPerPresence, GameConstants.ObjectSources.PresenceSource);
Debug.Log(this.gameObject.name + " has relative power level of: " + GetRelativePowerLevel());
onAllStatsUpdated.Invoke();
}
private void RemovePrimaryStatInfluences()
{
// Remove Cunning influences
RemoveStatInfluence("critchance", GameConstants.ObjectSources.CunningSource);
RemoveStatInfluence("critdamage", GameConstants.ObjectSources.CunningSource);
RemoveStatInfluence("movementspeed", GameConstants.ObjectSources.CunningSource);
// Remove Flow influences
RemoveStatInfluence("maxmana", GameConstants.ObjectSources.FlowSource);
RemoveStatInfluence("manaregen", GameConstants.ObjectSources.FlowSource);
RemoveStatInfluence("cooldownreduction", GameConstants.ObjectSources.FlowSource);
RemoveStatInfluence("attackspeed", GameConstants.ObjectSources.FlowSource);
// Remove Presence influences
RemoveStatInfluence("areaeffectiveness", GameConstants.ObjectSources.PresenceSource);
RemoveStatInfluence("reputationgainincrease", GameConstants.ObjectSources.PresenceSource);
RemoveStatInfluence("goldcostreduction", GameConstants.ObjectSources.PresenceSource);
RemoveStatInfluence("aurapower", GameConstants.ObjectSources.PresenceSource);
}
private void ApplyStatInfluence(string statKey, float value, object source)
{
var stat = GetStat(statKey);
if (stat != null)
{
stat.AddModifier(new StatModifier(value, StatModType.Flat, source));
}
else
{
Debug.LogWarning($"CharacterStats: Could not apply influence to stat '{statKey}' - stat not found!");
}
}
private void RemoveStatInfluence(string statKey, object source)
{
var stat = GetStat(statKey);
stat?.RemoveAllModifiersFromSource(source);
}
public float GetRelativePowerLevel()
{
var attackDamage = GetStatSafe("attackdamage");
var spellDamage = GetStatSafe("spelldamage");
var critDamage = GetStatSafe("critdamage");
var critChance = GetStatSafe("critchance");
var maxHealth = GetStatSafe("maxhealth");
var armor = GetStatSafe("armor");
var magicResistance = GetStatSafe("magicresistance");
return attackDamage.Value * 1.1f +
spellDamage.Value * 1.1f +
(critDamage.Value * (critChance.Value / 100f)) +
maxHealth.Value * 1f +
armor.Value * 1f +
magicResistance.Value * 1f;
}
// Debug/Editor methods
[ContextMenu("Log All Stats")]
private void LogAllStats()
{
Debug.Log($"=== CharacterStats on {gameObject.name} ===");
foreach (var kvp in allStats)
{
var stat = kvp.Value;
var def = stat.statDefinition;
Debug.Log($"{def.DisplayName} ({kvp.Key}): {stat.Value} (Base: {stat.BaseValue}, Category: {def.Category})");
}
}
[ContextMenu("Refresh Stats From Registry")]
private void RefreshStatsFromRegistry()
{
InitializeStatsFromRegistry();
onUpdateStatValues.Invoke();
}
}
}