| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427 |
- using UnityEngine;
- using System.Collections.Generic;
- using System.Linq;
- /// <summary>
- /// Manages character carry capacity and item weights
- /// Provides penalties for exceeding carry limits
- /// </summary>
- [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
- }
- /// <summary>
- /// Calculate maximum carry capacity for a character based on strength
- /// </summary>
- public int CalculateMaxCapacity(int strength)
- {
- return baseCarryCapacity + Mathf.RoundToInt(strength * strengthMultiplier);
- }
- /// <summary>
- /// Get current encumbrance level based on carried weight and capacity
- /// </summary>
- 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;
- }
- /// <summary>
- /// Get movement speed modifier based on encumbrance
- /// </summary>
- 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;
- }
- }
- /// <summary>
- /// Get dexterity modifier based on encumbrance
- /// </summary>
- 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;
- }
- }
- /// <summary>
- /// Get encumbrance status description
- /// </summary>
- 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";
- }
- }
- }
- /// <summary>
- /// Component for managing a character's carry capacity and current load
- /// Attach to Character objects that need inventory weight management
- /// </summary>
- 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 = false;
- private Character character;
- private Inventory inventory;
- // Events
- public event System.Action<CarryCapacitySystem.EncumbranceLevel> OnEncumbranceChanged;
- void Start()
- {
- character = GetComponent<Character>();
- inventory = GetComponent<Inventory>();
- if (character == null)
- {
- Debug.LogError($"CharacterCarryCapacity requires a Character component on {gameObject.name}");
- enabled = false;
- return;
- }
- UpdateCapacity();
- }
- /// <summary>
- /// Update maximum capacity based on current strength
- /// </summary>
- public void UpdateCapacity()
- {
- if (character == null) return;
- maxCapacity = carrySystem.CalculateMaxCapacity(character.Strength);
- UpdateCurrentWeight();
- if (showDebugInfo)
- Debug.Log($"📦 {character.CharacterName} capacity: {currentWeight}/{maxCapacity} lbs ({currentEncumbrance})");
- }
- /// <summary>
- /// Update current weight by calculating all carried items
- /// </summary>
- 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)}");
- }
- }
- /// <summary>
- /// Calculate total weight of all carried items
- /// </summary>
- private int CalculateTotalWeight()
- {
- int totalWeight = 0;
- // Calculate inventory weight if available
- if (inventory != null)
- {
- totalWeight += CalculateInventoryWeight();
- }
- // Calculate equipped item weight
- totalWeight += CalculateEquippedWeight();
- return totalWeight;
- }
- /// <summary>
- /// Calculate weight of items in inventory
- /// </summary>
- 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;
- }
- /// <summary>
- /// Calculate weight of equipped items
- /// </summary>
- 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;
- }
- /// <summary>
- /// Get weight of a specific item (with fallback values)
- /// </summary>
- 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
- }
- /// <summary>
- /// Get weapon weight based on name/type
- /// </summary>
- 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
- }
- /// <summary>
- /// Get armor weight based on type
- /// </summary>
- 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;
- }
- }
- /// <summary>
- /// Get miscellaneous item weight
- /// </summary>
- 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
- }
- /// <summary>
- /// Check if character can carry additional weight
- /// </summary>
- public bool CanCarryWeight(int additionalWeight)
- {
- return (currentWeight + additionalWeight) <= maxCapacity;
- }
- /// <summary>
- /// Get available carrying capacity
- /// </summary>
- public int GetAvailableCapacity()
- {
- return Mathf.Max(0, maxCapacity - currentWeight);
- }
- /// <summary>
- /// Get current movement speed modifier due to encumbrance
- /// </summary>
- public float GetMovementSpeedModifier()
- {
- return carrySystem.GetMovementSpeedModifier(currentEncumbrance);
- }
- /// <summary>
- /// Get current dexterity modifier due to encumbrance
- /// </summary>
- public int GetDexterityModifier()
- {
- return carrySystem.GetDexterityModifier(currentEncumbrance);
- }
- /// <summary>
- /// Get encumbrance info for UI display
- /// </summary>
- 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();
- }
- }
|