252 lines
10 KiB
C#

using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Events;
using Kryz.CharacterStats;
namespace Kryz.CharacterStats.Examples
{
public class CharacterStats : MonoBehaviour
{
[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();
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");
}
// 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();
}
}
}