387 lines
13 KiB
C#
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;
|
|
}
|
|
} |