using Kryz.CharacterStats.Examples; using System.Collections.Generic; using System.Linq; using UnityEngine; public class EquippableItemGenerator : MonoBehaviour { [Header("Generation Settings")] public float statVariance = 0.3f; // 30% variance public enum ItemTier { Common, Uncommon, Rare, Epic, Legendary } #region Singleton private static EquippableItemGenerator _instance; public static EquippableItemGenerator Instance { get { if (_instance == null) { _instance = FindFirstObjectByType(); } return _instance; } } #endregion private void Awake() { _instance = this; } [ContextMenu("Generate Random Item To Inventory")] private void TestGenerateItemToInventory() { // Get all available equipment types var allEquipment = EquipmentTypeRegistry.Instance.GetAllEquipment() .Where(eq => eq.CanBeGenerated) .ToArray(); if (allEquipment.Length == 0) { Debug.LogWarning("No equipment types available for generation!"); return; } // Pick random equipment type var randomEquipmentType = allEquipment[Random.Range(0, allEquipment.Length)]; var randomTier = (ItemTier)Random.Range(0, 5); var generatedItem = GenerateFromEquipmentType(randomEquipmentType, randomTier, 1); if (Inventory.Instance != null) { Inventory.Instance.AddItem(generatedItem); } } // Main generation method using EquipmentTypeDefinition public EquippableItemInstance GenerateFromEquipmentType(EquippableItemTypeDefinition equipmentTypeDef, ItemTier tier = ItemTier.Common, int playerLevel = 1) { // Get random icon and path var randomIcon = equipmentTypeDef.GetRandomIcon(); var iconPath = EquippableItemInstance.GetIconPathFromEquipmentType(equipmentTypeDef, randomIcon); // Generate item name string itemName = GenerateItemName(equipmentTypeDef, tier); // Create the item instance var item = new EquippableItemInstance(equipmentTypeDef, itemName, randomIcon, tier, iconPath); // Generate stats based on equipment rules GenerateStatsForItem(item, equipmentTypeDef, tier, playerLevel); Debug.Log($"Generated: {item.ItemName} ({equipmentTypeDef.EquipmentKey}) with {item.GetUniqueStatCount()} stats"); return item; } // Legacy methods for backward compatibility (deprecated) public EquippableItemInstance GenerateEquippableItemInstance(EquipmentType equipmentType, ItemTier tier = ItemTier.Common, int playerLevel = 1) { Debug.LogWarning("GenerateEquippableItemInstance(EquipmentType) is deprecated. Use GenerateFromEquipmentType instead."); // Try to find matching EquipmentTypeDefinition var equipmentKey = equipmentType.ToString().ToLower(); var equipmentTypeDef = EquipmentTypeRegistry.Instance.GetEquipment(equipmentKey); if (equipmentTypeDef != null) { return GenerateFromEquipmentType(equipmentTypeDef, tier, playerLevel); } // Fallback to basic generation if no definition found return CreateFallbackItem(equipmentType, tier, playerLevel); } public EquippableItemInstance GenerateWeapon(WeaponType weaponType, ItemTier tier = ItemTier.Common, int playerLevel = 1) { Debug.LogWarning("GenerateWeapon(WeaponType) is deprecated. Use GenerateFromEquipmentType instead."); // Try to find matching EquipmentTypeDefinition var weaponKey = weaponType.ToString().ToLower(); var equipmentTypeDef = EquipmentTypeRegistry.Instance.GetAllEquipment() .FirstOrDefault(eq => eq.IsWeapon && eq.WeaponType == weaponType); if (equipmentTypeDef != null) { return GenerateFromEquipmentType(equipmentTypeDef, tier, playerLevel); } // Fallback return CreateFallbackWeapon(weaponType, tier, playerLevel); } private void GenerateStatsForItem(EquippableItemInstance item, EquippableItemTypeDefinition equipmentTypeDef, ItemTier tier, int playerLevel) { // Get stat roll count from equipment definition or tier //int statCount = equipmentTypeDef.GetRandomStatRollCount(); int statCount = 0; if (statCount == 0) { statCount = GetStatCountForTier(tier); } float levelMultiplier = 1f + (playerLevel - 1) * 0.1f; // 10% increase per level float tierMultiplier = GetTierMultiplier(tier); float totalMultiplier = levelMultiplier * tierMultiplier; // Get all possible stats for this equipment type var availableStats = new List(); // First add mandatory stats foreach (var rule in equipmentTypeDef.StatRules) { if (rule.isMandatory && rule.stat != null) { availableStats.Add(rule.stat); } } // Then add other allowed stats foreach (var rule in equipmentTypeDef.StatRules) { if (rule.isAllowed && !rule.isMandatory && rule.stat != null && !availableStats.Contains(rule.stat)) { availableStats.Add(rule.stat); } } if (availableStats.Count == 0) { Debug.LogWarning($"No available stats for equipment type: {equipmentTypeDef.EquipmentKey}"); return; } // Roll stats var rolledStats = new HashSet(); // Apply mandatory stats first foreach (var rule in equipmentTypeDef.StatRules.Where(r => r.isMandatory && r.stat != null)) { ApplyStatToItem(item, rule, totalMultiplier); rolledStats.Add(rule.stat); } // Roll additional stats up to the stat count int additionalStatsNeeded = Mathf.Max(0, statCount - rolledStats.Count); for (int i = 0; i < additionalStatsNeeded && rolledStats.Count < availableStats.Count; i++) { var selectedStat = SelectWeightedRandomStat(equipmentTypeDef, rolledStats); if (selectedStat != null) { var rule = equipmentTypeDef.StatRules.FirstOrDefault(r => r.stat == selectedStat); if (rule != null) { ApplyStatToItem(item, rule, totalMultiplier); rolledStats.Add(selectedStat); } } } } private StatDefinition SelectWeightedRandomStat(EquippableItemTypeDefinition equipmentTypeDef, HashSet alreadyRolled) { var availableRules = equipmentTypeDef.StatRules .Where(rule => rule.isAllowed && rule.stat != null && !alreadyRolled.Contains(rule.stat)) .ToList(); if (availableRules.Count == 0) return null; // Build weighted list float totalWeight = 0f; var weightedRules = new List<(StatAvailabilityWeightAndRolls rule, float weight)>(); foreach (var rule in availableRules) { float weight = rule.weightMultiplier; if (weight > 0) { weightedRules.Add((rule, weight)); totalWeight += weight; } } if (totalWeight <= 0) return availableRules[Random.Range(0, availableRules.Count)].stat; // Weighted random selection float randomValue = Random.Range(0f, totalWeight); float currentWeight = 0f; foreach (var (rule, weight) in weightedRules) { currentWeight += weight; if (randomValue <= currentWeight) { return rule.stat; } } return weightedRules[0].rule.stat; // Fallback } private void ApplyStatToItem(EquippableItemInstance item, StatAvailabilityWeightAndRolls rule, float multiplier) { var statDef = rule.stat; // Determine whether to use flat or percent (or both) bool canUseFlat = statDef.CanBeFlat; bool canUsePercent = statDef.CanBePercent; // If both are possible, choose randomly (60% flat, 40% percent) bool useFlat = canUseFlat; bool usePercent = canUsePercent; if (canUseFlat && canUsePercent) { if(rule.forceUsePercent) { usePercent = true; useFlat = false; } else { usePercent = false; useFlat = true; } } float flatValue = 0f; float percentValue = 0f; if (useFlat) { flatValue = GenerateFlatStatValue(rule, multiplier); } if (usePercent) { percentValue = GeneratePercentStatValue(rule, multiplier); } // Add the stat to the item item.AddStatBonus(statDef, flatValue, percentValue); } private float GenerateFlatStatValue(StatAvailabilityWeightAndRolls rule, float multiplier) { float baseValue; // Apply stat-specific scaling from the rule if (rule.maxStatRoll > rule.minStatRoll) { baseValue = Random.Range(rule.minStatRoll, rule.maxStatRoll) * multiplier; } else { baseValue = Mathf.Min(rule.minStatRoll, rule.maxStatRoll) * multiplier; } // Add variance float variance = baseValue * statVariance; float finalValue = Random.Range(baseValue - variance, baseValue + variance); return Mathf.Max(1f, finalValue); } private float GeneratePercentStatValue(StatAvailabilityWeightAndRolls rule, float multiplier) { float baseValue; // Apply stat-specific scaling from the rule if (rule.maxPercentStatRoll > rule.minPercentStatRoll) { baseValue = Random.Range(rule.minPercentStatRoll, rule.maxPercentStatRoll) * multiplier; } else { baseValue = Mathf.Min(rule.minPercentStatRoll, rule.maxPercentStatRoll) * multiplier; } // Add variance float variance = baseValue * statVariance; float finalValue = Random.Range(baseValue - variance, baseValue + variance); return Mathf.Max(0.01f, Mathf.Round(finalValue * 1000f) / 1000f); // Round to 3 decimal places } // Utility methods private int GetStatCountForTier(ItemTier tier) { return tier switch { ItemTier.Common => Random.Range(1, 3), ItemTier.Uncommon => Random.Range(2, 4), ItemTier.Rare => Random.Range(3, 5), ItemTier.Epic => Random.Range(4, 6), ItemTier.Legendary => Random.Range(5, 7), _ => 2 }; } private float GetTierMultiplier(ItemTier tier) { return tier switch { ItemTier.Common => 1f, ItemTier.Uncommon => 1.3f, ItemTier.Rare => 1.7f, ItemTier.Epic => 2.2f, ItemTier.Legendary => 3f, _ => 1f }; } private string GenerateItemName(EquippableItemTypeDefinition equipmentTypeDef, ItemTier tier) { string[] tierPrefixes = tier switch { ItemTier.Common => new[] { "", "Simple", "Basic" }, ItemTier.Uncommon => new[] { "Fine", "Quality", "Enhanced" }, ItemTier.Rare => new[] { "Superior", "Masterwork", "Refined" }, ItemTier.Epic => new[] { "Exceptional", "Legendary", "Mythic" }, ItemTier.Legendary => new[] { "Divine", "Ancient", "Celestial" }, _ => new[] { "" } }; string prefix = tierPrefixes[Random.Range(0, tierPrefixes.Length)]; string baseName = equipmentTypeDef.GetDisplayName(); return string.IsNullOrEmpty(prefix) ? baseName : $"{prefix} {baseName}"; } // Fallback methods for legacy support private EquippableItemInstance CreateFallbackItem(EquipmentType equipmentType, ItemTier tier, int playerLevel) { var item = new EquippableItemInstance(); item.EquipmentType = equipmentType; item.ItemName = $"{tier} {equipmentType}"; Debug.LogWarning($"Created fallback item for {equipmentType} - consider creating an EquipmentTypeDefinition"); return item; } private EquippableItemInstance CreateFallbackWeapon(WeaponType weaponType, ItemTier tier, int playerLevel) { var item = new EquippableItemInstance(); item.EquipmentType = EquipmentType.Weapon1; item.WeaponType = weaponType; item.ItemName = $"{tier} {weaponType}"; Debug.LogWarning($"Created fallback weapon for {weaponType} - consider creating an EquipmentTypeDefinition"); return item; } }