252 lines
10 KiB
C#
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();
|
|
}
|
|
}
|
|
} |