281 lines
9.2 KiB
C#

using Kryz.CharacterStats;
using Kryz.CharacterStats.Examples;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
[System.Serializable]
public class ItemStatBonus
{
public StatDefinition statDefinition;
public float flatValue;
public float percentValue;
public bool HasFlatBonus => flatValue != 0;
public bool HasPercentBonus => percentValue != 0;
public bool HasAnyBonus => HasFlatBonus || HasPercentBonus;
public ItemStatBonus() { }
public ItemStatBonus(StatDefinition stat, float flat = 0f, float percent = 0f)
{
statDefinition = stat;
flatValue = flat;
percentValue = percent;
}
}
[System.Serializable]
public class EquippableItemInstance : ItemInstance
{
[Header("Equipment Identity")]
public EquipmentType EquipmentType;
public WeaponType WeaponType;
public string IconPath; // Store the icon path for serialization
[Header("Dynamic Stat System")]
public List<ItemStatBonus> statBonuses = new List<ItemStatBonus>();
[Header("Crafting System")]
public bool CraftableBase = false;
public int MaxTotalUniqueStatsIncreasedByStones;
public List<string> AddedStoneStats = new List<string>();
// Helper properties
public bool IsWeapon => EquipmentType == EquipmentType.Weapon1 || EquipmentType == EquipmentType.Weapon2;
public bool IsTwoHandedWeapon => IsWeapon && WeaponType.IsTwoHanded();
public bool IsOneHandedWeapon => IsWeapon && WeaponType.IsOneHanded();
public void Equip(PlayerCharacterStats characterStats)
{
foreach (var bonus in statBonuses)
{
if (bonus.statDefinition == null || !bonus.HasAnyBonus) continue;
var stat = characterStats.GetStat(bonus.statDefinition.StatKey);
if (stat != null)
{
// Apply flat bonus
if (bonus.HasFlatBonus)
{
stat.AddModifier(new StatModifier(bonus.flatValue, StatModType.Flat, this));
}
// Apply percent bonus
if (bonus.HasPercentBonus)
{
stat.AddModifier(new StatModifier(bonus.percentValue, StatModType.PercentAdd, this));
}
}
else
{
Debug.LogWarning($"EquippableItemInstance: Stat '{bonus.statDefinition.StatKey}' not found in character stats!");
}
}
}
public void Unequip(PlayerCharacterStats characterStats)
{
// Remove all modifiers from this item source across all stats
var allStats = characterStats.GetAllStats();
foreach (var kvp in allStats)
{
kvp.RemoveAllModifiersFromSource(this);
}
}
// Stat management methods
public void AddStatBonus(StatDefinition statDef, float flatValue = 0f, float percentValue = 0f)
{
var existingBonus = statBonuses.FirstOrDefault(b => b.statDefinition == statDef);
if (existingBonus != null)
{
existingBonus.flatValue += flatValue;
existingBonus.percentValue += percentValue;
}
else
{
statBonuses.Add(new ItemStatBonus(statDef, flatValue, percentValue));
}
}
public void SetStatBonus(StatDefinition statDef, float flatValue = 0f, float percentValue = 0f)
{
var existingBonus = statBonuses.FirstOrDefault(b => b.statDefinition == statDef);
if (existingBonus != null)
{
existingBonus.flatValue = flatValue;
existingBonus.percentValue = percentValue;
}
else
{
statBonuses.Add(new ItemStatBonus(statDef, flatValue, percentValue));
}
}
public ItemStatBonus GetStatBonus(StatDefinition statDef)
{
return statBonuses.FirstOrDefault(b => b.statDefinition == statDef);
}
public bool HasStat(StatDefinition statDef)
{
var bonus = GetStatBonus(statDef);
return bonus != null && bonus.HasAnyBonus;
}
public List<ItemStatBonus> GetAllNonZeroBonuses()
{
return statBonuses.Where(b => b.HasAnyBonus).ToList();
}
public int GetUniqueStatCount()
{
return GetAllNonZeroBonuses().Count;
}
// Crafting stone system (updated to use new stat system)
public bool CanAddCraftingStone(CraftingStatStone stone)
{
if (!CraftableBase) return false;
var stoneStats = GetNonZeroStatsFromStone(stone);
var currentStats = GetAllNonZeroBonuses().Select(b => b.statDefinition.StatKey).ToList();
int newUniqueStats = stoneStats.Except(currentStats).Count();
// Ensure we don't exceed the max unique stats cap
if (AddedStoneStats.Count + newUniqueStats > MaxTotalUniqueStatsIncreasedByStones)
{
return false;
}
return true;
}
public bool TryAddCraftingStone(CraftingStatStone stone)
{
if (!CanAddCraftingStone(stone)) return false;
var stoneStats = GetNonZeroStatsFromStone(stone);
var currentStats = GetAllNonZeroBonuses().Select(b => b.statDefinition.StatKey).ToList();
// Track new stats being added
foreach (var statKey in stoneStats)
{
if (!currentStats.Contains(statKey) && !AddedStoneStats.Contains(statKey))
{
AddedStoneStats.Add(statKey);
}
}
// Add stats from the stone to the item (this needs to be updated based on your stone structure)
AddStatsFromStone(stone);
return true;
}
private List<string> GetNonZeroStatsFromStone(CraftingStatStone stone)
{
var nonZeroStats = new List<string>();
var fields = stone.GetType().GetFields();
foreach (var field in fields)
{
if (field.GetValue(stone) is int intValue && intValue != 0 && field.Name.ToLower().Contains("bonus"))
{
nonZeroStats.Add(field.Name);
}
else if (field.GetValue(stone) is float floatValue && !Mathf.Approximately(floatValue, 0f) && field.Name.ToLower().Contains("bonus"))
{
nonZeroStats.Add(field.Name);
}
}
return nonZeroStats;
}
// This method needs to be updated based on your CraftingStatStone structure
private void AddStatsFromStone(CraftingStatStone stone)
{
// TODO: Update this to work with the new stat system
// You'll need to map stone properties to StatDefinition references
// For now, keeping the old structure as a placeholder
Debug.LogWarning("AddStatsFromStone needs to be updated for the new stat system");
}
// Constructors
public EquippableItemInstance()
{
EquipmentType = EquipmentType.Helmet;
WeaponType = WeaponType.Sword;
CraftableBase = false;
MaxTotalUniqueStatsIncreasedByStones = 0;
statBonuses = new List<ItemStatBonus>();
AddedStoneStats = new List<string>();
templateIndex = -1;
}
// Constructor for generating from EquipmentTypeDefinition (recommended approach)
public EquippableItemInstance(EquippableItemTypeDefinition equipmentTypeDef, string itemName, Sprite icon, string iconPath = null)
{
ItemName = itemName;
Icon = icon;
IconPath = iconPath ?? ""; // Store the icon path
EquipmentType = equipmentTypeDef.EquipmentType;
WeaponType = equipmentTypeDef.WeaponType;
CraftableBase = false; // Set based on your needs
MaxTotalUniqueStatsIncreasedByStones = 6; // Default value
statBonuses = new List<ItemStatBonus>();
AddedStoneStats = new List<string>();
templateIndex = -1;
// You can set default sell prices here or pass them as parameters
sellPricePlayer = 100;
sellPriceVendor = 50;
description = $"A {equipmentTypeDef.GetDisplayName()}";
}
// Helper method to set icon with path
public void SetIcon(Sprite icon, string iconPath)
{
Icon = icon;
IconPath = iconPath;
}
// Helper method to get icon path from EquipmentTypeDefinition
public static string GetIconPathFromEquipmentType(EquippableItemTypeDefinition equipmentTypeDef, Sprite icon)
{
if (equipmentTypeDef.UseResourcesFolder && !string.IsNullOrEmpty(equipmentTypeDef.ResourcesPath))
{
return $"{equipmentTypeDef.ResourcesPath}/{icon.name}";
}
return ""; // Manual icons don't have paths
}
// Utility methods for debugging and UI
public string GetStatSummary()
{
if (statBonuses.Count == 0) return "No stat bonuses";
var summary = new List<string>();
foreach (var bonus in GetAllNonZeroBonuses())
{
var parts = new List<string>();
if (bonus.HasFlatBonus)
parts.Add($"+{bonus.flatValue:F0}");
if (bonus.HasPercentBonus)
parts.Add($"+{bonus.percentValue:P1}");
summary.Add($"{bonus.statDefinition.DisplayName}: {string.Join(" ", parts)}");
}
return string.Join("\n", summary);
}
[ContextMenu("Log Stat Summary")]
private void LogStatSummary()
{
Debug.Log($"=== {ItemName} Stats ===\n{GetStatSummary()}");
}
}