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