Files
BABA_YAGA/Assets/Opsive/UltimateCharacterController/Scripts/Inventory/Inventory.cs
2026-06-09 02:05:00 +07:00

273 lines
12 KiB
C#

/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.Inventory
{
using Opsive.Shared.Inventory;
using Opsive.UltimateCharacterController.Items;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Implements InventoryBase - adds a basic inventory to the character controller.
/// </summary>
public class Inventory : InventoryBase
{
[Tooltip("Items to load when the Inventory is initially created or on a character respawn.")]
[UnityEngine.Serialization.FormerlySerializedAs("m_ItemTypeCount")]
[SerializeField] protected ItemDefinitionAmount[] m_DefaultLoadout;
private Dictionary<IItemIdentifier, Item>[] m_ItemIdentifierMap;
private Dictionary<IItemIdentifier, int> m_ItemIdentifierAmount = new Dictionary<IItemIdentifier, int>();
private Item[] m_ActiveItem;
public ItemDefinitionAmount[] DefaultLoadout { get { return m_DefaultLoadout; } set { m_DefaultLoadout = value; } }
/// <summary>
/// Initialize the default values.
/// </summary>
protected override void Awake()
{
base.Awake();
m_ItemIdentifierMap = new Dictionary<IItemIdentifier, Item>[m_SlotCount];
for (int i = 0; i < m_SlotCount; ++i) {
m_ItemIdentifierMap[i] = new Dictionary<IItemIdentifier, Item>();
}
m_ActiveItem = new Item[m_SlotCount];
}
/// <summary>
/// Pick up each ItemIdentifier within the DefaultLoadout.
/// </summary>
public override void LoadDefaultLoadout()
{
if (m_DefaultLoadout != null) {
for (int i = 0; i < m_DefaultLoadout.Length; ++i) {
Pickup(m_DefaultLoadout[i].ItemIdentifier, m_DefaultLoadout[i].Amount, -1, true, false);
}
}
}
/// <summary>
/// Internal method which determines if the character has the specified item.
/// </summary>
/// <param name="item">The item to check against.</param>
/// <returns>True if the character has the item.</returns>
protected override bool HasItemInternal(Item item)
{
if (item == null) {
return false;
}
return GetItemInternal(item.ItemIdentifier, item.SlotID) != null;
}
/// <summary>
/// Adds the item to the inventory. This does not add the actual ItemIdentifier - PickupItem does that.
/// </summary>
/// <param name="item">The item to add.</param>
/// <returns>True if the item was added to the inventory.</returns>
protected override bool AddItemInternal(Item item)
{
if (item.ItemDefinition == null) {
Debug.LogError($"Error: Item {item.gameObject.name} has no ItemDefinition.");
return false;
}
if (m_ItemIdentifierMap == null) {
Debug.LogError($"Error: Unable to add {item.gameObject.name} because the inventory component doesn't exist.");
return false;
}
if (item.SlotID >= m_ItemIdentifierMap.Length) {
Debug.LogError($"Error: Unable to add {item.gameObject.name} because the slot id is greater than the number of slots that exist in the inventory.");
return false;
}
var itemIdentifier = item.ItemIdentifier;
if (itemIdentifier == null) {
itemIdentifier = item.ItemDefinition.CreateItemIdentifier();
}
// The item may already exist in the inventory.
if (m_ItemIdentifierMap[item.SlotID].ContainsKey(itemIdentifier)) {
return false;
}
// The item doesn't exist - add it.
m_ItemIdentifierMap[item.SlotID].Add(itemIdentifier, item);
// The item can be added without being picked up yet - add to the ItemIdentifierAmount so the item can safely be removed.
if (!m_ItemIdentifierAmount.ContainsKey(itemIdentifier)) {
m_ItemIdentifierAmount.Add(itemIdentifier, 0);
}
return true;
}
/// <summary>
/// Adds the specified amount of the ItemIdentifier to the inventory.
/// </summary>
/// <param name="itemIdentifier">The ItemIdentifier to add.</param>
/// <param name="amount">The amount of ItemIdentifier to add.</param>
/// <returns>True if the ItemIdentifier was picked up.</returns>
protected override bool PickupInternal(IItemIdentifier itemIdentifier, int amount)
{
if (!m_ItemIdentifierAmount.TryGetValue(itemIdentifier, out var existingAmount)) {
if (itemIdentifier is ItemType) {
var itemType = itemIdentifier as ItemType;
m_ItemIdentifierAmount.Add(itemIdentifier, Mathf.Min(amount, itemType.Capacity));
} else {
m_ItemIdentifierAmount.Add(itemIdentifier, amount);
}
} else {
if (itemIdentifier is ItemType) {
var itemType = itemIdentifier as ItemType;
// The ItemType was not picked up if it is already at capacity.
if (existingAmount == itemType.Capacity) {
return false;
}
m_ItemIdentifierAmount[itemIdentifier] = Mathf.Clamp(existingAmount + amount, 0, itemType.Capacity);
} else {
m_ItemIdentifierAmount[itemIdentifier] = existingAmount + amount;
}
}
return true;
}
/// <summary>
/// Internal method which returns the active item in the specified slot.
/// </summary>
/// <param name="slotID">The ID of the slot which the item belongs to.</param>
/// <returns>The active item which occupies the specified slot. Can be null.</returns>
protected override Item GetActiveItemInternal(int slotID)
{
if (slotID < -1 || slotID >= m_ItemIdentifierMap.Length) {
return null;
}
return m_ActiveItem[slotID];
}
/// <summary>
/// Internal method which returns the item that corresponds to the specified ItemIdentifier.
/// </summary>
/// <param name="slotID">The ID of the slot which the item belongs to.</param>
/// <param name="itemIdentifier">The ItemIdentifier of the item.</param>
/// <returns>The item which occupies the specified slot. Can be null.</returns>
protected override Item GetItemInternal(IItemIdentifier itemIdentifier, int slotID)
{
if (itemIdentifier == null || slotID < -1 || slotID >= m_ItemIdentifierMap.Length) {
return null;
}
if (m_ItemIdentifierMap[slotID].TryGetValue(itemIdentifier, out var item)) {
return item;
}
return null;
}
/// <summary>
/// Internal method which equips the ItemIdentifier in the specified slot.
/// </summary>
/// <param name="itemIdentifier">The ItemIdentifier to equip.</param>
/// <param name="slotID">The ID of the slot.</param>
/// <returns>The item which corresponds to the ItemIdentifier. Can be null.</returns>
protected override Item EquipItemInternal(IItemIdentifier itemIdentifier, int slotID)
{
if (itemIdentifier == null || slotID < -1 || slotID >= m_ItemIdentifierMap.Length) {
return null;
}
// The ItemIdentifier has to exist in the inventory.
if (!m_ItemIdentifierMap[slotID].TryGetValue(itemIdentifier, out var item)) {
Debug.LogError($"Error: Unable to equip item with ItemIdentifier {itemIdentifier}: the itemIdentifier hasn't been added to the inventory.");
return null;
}
m_ActiveItem[slotID] = item;
return item;
}
/// <summary>
/// Internal method which unequips the item in the specified slot.
/// </summary>
/// <param name="slotID">The ID of the slot.</param>
/// <returns>The item that was unequipped.</returns>
protected override Item UnequipItemInternal(int slotID)
{
if (slotID < -1 || slotID >= m_ItemIdentifierMap.Length) {
return null;
}
var prevItem = m_ActiveItem[slotID];
m_ActiveItem[slotID] = null;
return prevItem;
}
/// <summary>
/// Internal method which returns the amount of the specified ItemIdentifier.
/// </summary>
/// <param name="itemIdentifier">The ItemIdentifier to get the amount of.</param>
/// <returns>The amount of the specified ItemIdentifier.</returns>
protected override int GetItemIdentifierAmountInternal(IItemIdentifier itemIdentifier)
{
m_ItemIdentifierAmount.TryGetValue(itemIdentifier, out var amount);
return amount;
}
/// <summary>
/// Internal method which adjusts the amount of the specified ItemIdentifier.
/// </summary>
/// <param name="itemIdentifier">The ItemIdentifier to adjust.</param>
/// <param name="amount">The amount of ItemIdentifier to adjust.</param>
protected override void AdjustItemIdentifierAmountInternal(IItemIdentifier itemIdentifier, int amount)
{
if (!m_ItemIdentifierAmount.TryGetValue(itemIdentifier, out var existingAmount)) {
Debug.LogError($"Error: Trying to use item {itemIdentifier} when the ItemIdentifier doesn't exist.");
return;
}
if (itemIdentifier is ItemType) {
var itemType = itemIdentifier as ItemType;
m_ItemIdentifierAmount[itemIdentifier] = Mathf.Clamp(existingAmount + amount, 0, itemType.Capacity);
} else {
m_ItemIdentifierAmount[itemIdentifier] = existingAmount + amount;
}
}
/// <summary>
/// Internal method which removes the ItemIdentifier from the inventory.
/// </summary>
/// <param name="itemIdentifier">The ItemIdentifier to remove.</param>
/// <param name="slotID">The ID of the slot.</param>
/// <param name="amount">The amount of ItemIdentifier that should be removed.</param>
/// <returns>The item that was removed (can be null).</returns>
protected override void RemoveItemIdentifierInternal(IItemIdentifier itemIdentifier, int slotID, int amount)
{
if (itemIdentifier == null || !(itemIdentifier is ItemType) || slotID < -1 ||
slotID >= m_ItemIdentifierMap.Length || !m_ItemIdentifierAmount.TryGetValue(itemIdentifier, out var existingAmount)) {
return;
}
if (!m_ItemIdentifierMap[slotID].TryGetValue(itemIdentifier, out var item)) {
// Remove the ItemIdentifier. This ItemIdentifier does not correspond to an item so it should be completely removed.
m_ItemIdentifierAmount[itemIdentifier] = 0;
return;
}
// Remove a single Item. The character may be carrying multiple of the same ItemIdentifier in the case of dual wielding.
var itemType = itemIdentifier as ItemType;
m_ItemIdentifierAmount[itemType] = Mathf.Clamp(item.FullInventoryDrop ? 0 : (existingAmount - 1), 0, itemType.Capacity);
if (slotID == -1) {
return;
}
// The item should no longer be equipped.
if (m_ActiveItem[slotID] != null && m_ActiveItem[slotID].ItemIdentifier == itemIdentifier) {
m_ActiveItem[slotID] = null;
}
}
}
}