using UnityEngine; using System.Collections.Generic; using System.Linq; /// /// Manages character carry capacity and item weights /// Provides penalties for exceeding carry limits /// [System.Serializable] public class CarryCapacitySystem { [Header("Base Capacity Settings")] [Tooltip("Base carry capacity in pounds")] public int baseCarryCapacity = 50; [Tooltip("How much strength affects carry capacity")] public float strengthMultiplier = 5f; [Header("Encumbrance Thresholds")] [Tooltip("Light load threshold (fraction of max capacity)")] [Range(0f, 1f)] public float lightLoadThreshold = 0.33f; [Tooltip("Medium load threshold (fraction of max capacity)")] [Range(0f, 1f)] public float mediumLoadThreshold = 0.66f; [Header("Encumbrance Penalties")] [Tooltip("Movement speed penalty for medium load (%)")] [Range(0f, 1f)] public float mediumLoadMovementPenalty = 0.25f; [Tooltip("Movement speed penalty for heavy load (%)")] [Range(0f, 1f)] public float heavyLoadMovementPenalty = 0.5f; [Tooltip("Dexterity penalty for heavy load")] public int heavyLoadDexterityPenalty = 3; public enum EncumbranceLevel { Light, // 0-33% capacity Medium, // 34-66% capacity Heavy, // 67-100% capacity Overloaded // 100%+ capacity } /// /// Calculate maximum carry capacity for a character based on strength /// public int CalculateMaxCapacity(int strength) { return baseCarryCapacity + Mathf.RoundToInt(strength * strengthMultiplier); } /// /// Get current encumbrance level based on carried weight and capacity /// public EncumbranceLevel GetEncumbranceLevel(int currentWeight, int maxCapacity) { if (currentWeight > maxCapacity) return EncumbranceLevel.Overloaded; float loadRatio = (float)currentWeight / maxCapacity; if (loadRatio <= lightLoadThreshold) return EncumbranceLevel.Light; else if (loadRatio <= mediumLoadThreshold) return EncumbranceLevel.Medium; else return EncumbranceLevel.Heavy; } /// /// Get movement speed modifier based on encumbrance /// public float GetMovementSpeedModifier(EncumbranceLevel encumbrance) { switch (encumbrance) { case EncumbranceLevel.Light: return 1.0f; // No penalty case EncumbranceLevel.Medium: return 1.0f - mediumLoadMovementPenalty; case EncumbranceLevel.Heavy: return 1.0f - heavyLoadMovementPenalty; case EncumbranceLevel.Overloaded: return 0.25f; // Severe penalty for being overloaded default: return 1.0f; } } /// /// Get dexterity modifier based on encumbrance /// public int GetDexterityModifier(EncumbranceLevel encumbrance) { switch (encumbrance) { case EncumbranceLevel.Light: case EncumbranceLevel.Medium: return 0; // No penalty case EncumbranceLevel.Heavy: return -heavyLoadDexterityPenalty; case EncumbranceLevel.Overloaded: return -heavyLoadDexterityPenalty * 2; // Double penalty default: return 0; } } /// /// Get encumbrance status description /// public string GetEncumbranceDescription(EncumbranceLevel encumbrance) { switch (encumbrance) { case EncumbranceLevel.Light: return "Light Load - No penalties"; case EncumbranceLevel.Medium: return $"Medium Load - {mediumLoadMovementPenalty * 100:F0}% movement penalty"; case EncumbranceLevel.Heavy: return $"Heavy Load - {heavyLoadMovementPenalty * 100:F0}% movement, -{heavyLoadDexterityPenalty} DEX penalty"; case EncumbranceLevel.Overloaded: return $"Overloaded! - Severe penalties, consider dropping items"; default: return "Unknown"; } } } /// /// Component for managing a character's carry capacity and current load /// Attach to Character objects that need inventory weight management /// public class CharacterCarryCapacity : MonoBehaviour { [Header("Carry Capacity System")] public CarryCapacitySystem carrySystem = new CarryCapacitySystem(); [Header("Current Load Info (Read-Only)")] [SerializeField] private int currentWeight = 0; [SerializeField] private int maxCapacity = 0; [SerializeField] private CarryCapacitySystem.EncumbranceLevel currentEncumbrance = CarryCapacitySystem.EncumbranceLevel.Light; [Header("Debug")] public bool showDebugInfo = true; private Character character; private Inventory inventory; // Events public event System.Action OnEncumbranceChanged; void Start() { character = GetComponent(); inventory = GetComponent(); if (character == null) { Debug.LogError($"CharacterCarryCapacity requires a Character component on {gameObject.name}"); enabled = false; return; } UpdateCapacity(); } /// /// Update maximum capacity based on current strength /// public void UpdateCapacity() { if (character == null) return; maxCapacity = carrySystem.CalculateMaxCapacity(character.Strength); UpdateCurrentWeight(); if (showDebugInfo) Debug.Log($"📦 {character.CharacterName} capacity: {currentWeight}/{maxCapacity} lbs ({currentEncumbrance})"); } /// /// Update current weight by calculating all carried items /// public void UpdateCurrentWeight() { currentWeight = CalculateTotalWeight(); var previousEncumbrance = currentEncumbrance; currentEncumbrance = carrySystem.GetEncumbranceLevel(currentWeight, maxCapacity); if (previousEncumbrance != currentEncumbrance) { OnEncumbranceChanged?.Invoke(currentEncumbrance); if (showDebugInfo) Debug.Log($"📦 {character.CharacterName} encumbrance changed to: {carrySystem.GetEncumbranceDescription(currentEncumbrance)}"); } } /// /// Calculate total weight of all carried items /// private int CalculateTotalWeight() { int totalWeight = 0; // Calculate inventory weight if available if (inventory != null) { totalWeight += CalculateInventoryWeight(); } // Calculate equipped item weight totalWeight += CalculateEquippedWeight(); return totalWeight; } /// /// Calculate weight of items in inventory /// private int CalculateInventoryWeight() { int weight = 0; // Weapons foreach (var slot in inventory.Weapons) { if (slot.item is WeaponItem weapon) { weight += GetItemWeight(weapon) * slot.quantity; } } // Armor foreach (var slot in inventory.Armor) { if (slot.item is ArmorItem armor && !slot.isEquipped) // Don't count equipped armor twice { weight += GetItemWeight(armor) * slot.quantity; } } // Miscellaneous items foreach (var slot in inventory.Miscellaneous) { if (slot.item is MiscellaneousItem misc) { weight += GetItemWeight(misc) * slot.quantity; } } return weight; } /// /// Calculate weight of equipped items /// private int CalculateEquippedWeight() { int weight = 0; if (inventory != null) { // Equipped weapon if (inventory.EquippedWeapon != null) { weight += GetItemWeight(inventory.EquippedWeapon); } // Equipped armor var equippedArmor = inventory.GetEquippedArmor(); foreach (var armor in equippedArmor) { weight += GetItemWeight(armor); } } return weight; } /// /// Get weight of a specific item (with fallback values) /// private int GetItemWeight(Item item) { // TODO: When items get weight properties, use those // For now, use reasonable defaults based on item type if (item is WeaponItem weapon) { return GetWeaponWeight(weapon.itemName); } else if (item is ArmorItem armor) { return GetArmorWeight(armor.itemName, armor.armorType); } else if (item is MiscellaneousItem misc) { return GetMiscWeight(misc.itemName); } return 1; // Default weight } /// /// Get weapon weight based on name/type /// private int GetWeaponWeight(string weaponName) { string lower = weaponName.ToLower(); if (lower.Contains("dagger") || lower.Contains("knife")) return 1; else if (lower.Contains("sword") || lower.Contains("axe")) return 3; else if (lower.Contains("bow")) return 2; else if (lower.Contains("staff") || lower.Contains("wand")) return 2; else if (lower.Contains("hammer") || lower.Contains("mace")) return 4; else if (lower.Contains("fist") || lower.Contains("unarmed")) return 0; return 3; // Default sword weight } /// /// Get armor weight based on type /// private int GetArmorWeight(string armorName, ArmorType armorType) { switch (armorType) { case ArmorType.Light: return 5; case ArmorType.Medium: return 15; case ArmorType.Heavy: return 25; case ArmorType.Shield: return 3; default: return 10; } } /// /// Get miscellaneous item weight /// private int GetMiscWeight(string itemName) { string lower = itemName.ToLower(); if (lower.Contains("potion") || lower.Contains("scroll")) return 0; // Negligible weight else if (lower.Contains("rope")) return 10; else if (lower.Contains("torch")) return 1; else if (lower.Contains("ration") || lower.Contains("bread")) return 2; else if (lower.Contains("bandage")) return 0; else if (lower.Contains("tool")) return 5; return 1; // Default misc weight } /// /// Check if character can carry additional weight /// public bool CanCarryWeight(int additionalWeight) { return (currentWeight + additionalWeight) <= maxCapacity; } /// /// Get available carrying capacity /// public int GetAvailableCapacity() { return Mathf.Max(0, maxCapacity - currentWeight); } /// /// Get current movement speed modifier due to encumbrance /// public float GetMovementSpeedModifier() { return carrySystem.GetMovementSpeedModifier(currentEncumbrance); } /// /// Get current dexterity modifier due to encumbrance /// public int GetDexterityModifier() { return carrySystem.GetDexterityModifier(currentEncumbrance); } /// /// Get encumbrance info for UI display /// public string GetEncumbranceInfo() { return $"{currentWeight}/{maxCapacity} lbs - {carrySystem.GetEncumbranceDescription(currentEncumbrance)}"; } // Properties for external access public int CurrentWeight => currentWeight; public int MaxCapacity => maxCapacity; public CarryCapacitySystem.EncumbranceLevel CurrentEncumbrance => currentEncumbrance; // Manual weight update for when inventory changes public void RefreshWeight() { UpdateCurrentWeight(); } }