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