RiftMayhem/Assets/Scripts/Items/Generator/EquippableItemGenerator.cs

387 lines
13 KiB
C#

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<EquippableItemGenerator>();
}
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<StatDefinition>();
// 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<StatDefinition>();
// 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<StatDefinition> 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;
}
}