This commit is contained in:
jze9
2026-04-15 02:56:11 +05:00
parent 523bf475ca
commit 0837589fa2
1786 changed files with 236305 additions and 73082 deletions

View File

@@ -0,0 +1,601 @@
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.Events;
public class InventoryManager : MonoBehaviour
{
public static InventoryManager Instance { get; private set; }
[Header("Inventory Settings")]
public int inventorySize = 36;
public Slot[] slots;
public Slot[] hotbarSlots; // 9 слотов хотбара (индексы 0-8)
[Header("Item Database")]
public List<ItemData> itemDatabase;
[Header("References")]
public GameObject inventoryPanel;
public Canvas inventoryCanvas;
[Header("Events")]
/// <param name="itemId">ID предмета</param>
/// <param name="amount">Количество</param>
public UnityEvent<string, int> OnItemAdded;
public UnityEvent<string, int> OnItemRemoved;
// Приватные поля
private Dictionary<string, ItemData> _itemDict;
private int _selectedHotbarSlot = 0;
void Awake()
{
// Сделать этот объект корневым для DontDestroyOnLoad
transform.SetParent(null);
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
return;
}
// Создаём InventoryItemFactory если его нет
if (InventoryItemFactory.Instance == null)
{
var factoryGO = new GameObject("InventoryItemFactory");
factoryGO.AddComponent<InventoryItemFactory>();
DontDestroyOnLoad(factoryGO);
}
// Словарь создаём лениво
RebuildItemDictionary();
}
/// <summary>
/// Пересоздать словарь предметов из itemDatabase
/// </summary>
public void RebuildItemDictionary()
{
_itemDict = new Dictionary<string, ItemData>();
if (itemDatabase != null)
{
foreach (var item in itemDatabase)
{
if (!string.IsNullOrEmpty(item?.id))
{
_itemDict[item.id] = item;
}
}
}
Debug.Log($"[InventoryManager] Loaded {_itemDict.Count} items into dictionary");
}
void Start()
{
// Инициализация слотов
if (slots == null || slots.Length == 0)
{
Debug.LogWarning("Slots not assigned! Assign them in Inspector or use InventoryUIBuilder.");
}
else
{
foreach (var slot in slots)
{
slot?.UpdateSlotUI();
}
}
if (hotbarSlots != null)
{
foreach (var slot in hotbarSlots)
{
slot?.UpdateSlotUI();
}
}
// Скрыть инвентарь по умолчанию
if (inventoryPanel != null)
inventoryPanel.SetActive(false);
// Убедимся что события существуют
if (InventoryEvents.Instance == null)
{
var eventsGO = new GameObject("InventoryEvents");
eventsGO.AddComponent<InventoryEvents>();
}
}
void Update()
{
// Toggle инвентаря на Tab
if (Input.GetKeyDown(KeyCode.Tab))
{
ToggleInventory();
}
// Закрыть на Escape
if (Input.GetKeyDown(KeyCode.Escape) && inventoryPanel != null && inventoryPanel.activeSelf)
{
ToggleInventory();
}
// Горячие клавиши 1-9 для хотбара
for (int i = 0; i < 9; i++)
{
if (Input.GetKeyDown(KeyCode.Alpha1 + i))
{
SelectHotbarSlot(i);
}
}
// Выброс предмета на Q
if (Input.GetKeyDown(KeyCode.Q))
{
DropSelectedItem();
}
}
public void ToggleInventory()
{
if (inventoryPanel != null)
{
bool wasActive = inventoryPanel.activeSelf;
inventoryPanel.SetActive(!wasActive);
if (!wasActive)
{
Cursor.lockState = CursorLockMode.None;
InventoryEvents.Instance?.OnInventoryOpened?.Invoke();
}
else
{
Cursor.lockState = CursorLockMode.Locked;
InventoryEvents.Instance?.OnInventoryClosed?.Invoke();
}
}
}
public void SelectHotbarSlot(int index)
{
if (index < 0 || index >= 9 || hotbarSlots == null || hotbarSlots.Length < 9)
return;
// Снимаем выделение со старого
if (_selectedHotbarSlot >= 0 && _selectedHotbarSlot < hotbarSlots.Length)
{
hotbarSlots[_selectedHotbarSlot].SetSelected(false);
}
_selectedHotbarSlot = index;
hotbarSlots[index].SetSelected(true);
// Получаем выбранный предмет
var selectedItem = hotbarSlots[index].item;
Debug.Log($"[Inventory] Selected hotbar slot: {index} - {selectedItem?.itemName ?? "Empty"}");
// TODO: Эвент для использования предмета
}
/// <summary>
/// Получить предмет в выбранном слоте хотбара
/// </summary>
public ItemData GetSelectedHotbarItem()
{
if (hotbarSlots != null && _selectedHotbarSlot >= 0 && _selectedHotbarSlot < hotbarSlots.Length)
{
return hotbarSlots[_selectedHotbarSlot].item;
}
return null;
}
/// <summary>
/// Выбросить выбранный предмет из хотбара
/// </summary>
public void DropSelectedItem()
{
if (hotbarSlots == null || _selectedHotbarSlot >= hotbarSlots.Length)
return;
var slot = hotbarSlots[_selectedHotbarSlot];
if (slot.IsEmpty) return;
var item = slot.item;
int amount = slot.amount;
// Создаём предмет в мире через factory
if (InventoryItemFactory.Instance != null)
{
// Используем позицию игрока
Vector3 dropPos = Vector3.zero; // Заменить на позицию игрока
if (Camera.main != null)
{
dropPos = Camera.main.transform.position + Camera.main.transform.forward * 2f;
}
InventoryItemFactory.Instance.DropItem(item, amount, dropPos);
}
// Очищаем слот
slot.Clear();
Debug.Log($"[Inventory] Dropped {item.itemName} x{amount}");
}
// ==================== ОСНОВНЫЕ ОПЕРАЦИИ ====================
/// <summary>
/// Добавить предмет в инвентарь
/// </summary>
/// <returns>Количество не поместившихся предметов</returns>
public int AddItem(string itemId, int amount = 1)
{
if (!_itemDict.TryGetValue(itemId, out ItemData item))
{
Debug.LogWarning($"Item not found: {itemId}");
return amount;
}
return AddItem(item, amount);
}
public int AddItem(ItemData item, int amount = 1)
{
if (item == null)
{
Debug.LogWarning("[AddItem] item is null!");
return amount;
}
Debug.Log($"[AddItem] Adding {item.itemName} x{amount}, slots count: {slots?.Length ?? 0}");
if (slots == null || slots.Length == 0)
{
Debug.LogWarning("[AddItem] Slots not initialized!");
return amount;
}
// Сначала пытаемся доложить в существующие стеки (основной инвентарь)
foreach (var slot in slots)
{
if (slot.IsEmpty) continue;
if (slot.item.id == item.id)
{
amount = slot.AddItems(item, amount);
if (amount <= 0)
{
OnItemAdded?.Invoke(item.id, amount);
InventoryEvents.Instance?.InvokeItemAdded(item.id, slot.amount);
return 0;
}
}
}
// Добавляем в пустые слоты (основной инвентарь)
foreach (var slot in slots)
{
if (slot.IsEmpty)
{
amount = slot.AddItems(item, amount);
if (amount <= 0)
{
OnItemAdded?.Invoke(item.id, amount);
InventoryEvents.Instance?.InvokeItemAdded(item.id, slot.amount);
return 0;
}
}
}
// Пробуем хотбар
foreach (var slot in hotbarSlots)
{
if (!slot.IsEmpty && slot.item.id == item.id)
{
amount = slot.AddItems(item, amount);
if (amount <= 0)
{
OnItemAdded?.Invoke(item.id, amount);
InventoryEvents.Instance?.InvokeItemAdded(item.id, slot.amount);
return 0;
}
}
}
foreach (var slot in hotbarSlots)
{
if (slot.IsEmpty)
{
amount = slot.AddItems(item, amount);
if (amount <= 0)
{
OnItemAdded?.Invoke(item.id, amount);
InventoryEvents.Instance?.InvokeItemAdded(item.id, slot.amount);
return 0;
}
}
}
return amount;
}
/// <summary>
/// Удалить предмет из инвентаря
/// </summary>
public bool RemoveItem(string itemId, int amount = 1)
{
int remaining = amount;
foreach (var slot in slots)
{
if (slot.item != null && slot.item.id == itemId)
{
if (slot.amount >= remaining)
{
var (item, taken) = slot.TakeItems(remaining);
OnItemRemoved?.Invoke(itemId, taken);
InventoryEvents.Instance?.InvokeItemRemoved(itemId, taken);
return true;
}
else
{
remaining -= slot.amount;
slot.Clear();
OnItemRemoved?.Invoke(itemId, slot.amount);
InventoryEvents.Instance?.InvokeItemRemoved(itemId, slot.amount);
}
}
}
// Пробуем хотбар
if (remaining > 0)
{
foreach (var slot in hotbarSlots)
{
if (slot.item != null && slot.item.id == itemId)
{
if (slot.amount >= remaining)
{
var (item, taken) = slot.TakeItems(remaining);
OnItemRemoved?.Invoke(itemId, taken);
InventoryEvents.Instance?.InvokeItemRemoved(itemId, taken);
return true;
}
else
{
remaining -= slot.amount;
slot.Clear();
OnItemRemoved?.Invoke(itemId, slot.amount);
InventoryEvents.Instance?.InvokeItemRemoved(itemId, slot.amount);
}
}
}
}
return remaining <= 0;
}
/// <summary>
/// Получить количество предмета в инвентаре
/// </summary>
public int GetItemCount(string itemId)
{
int count = 0;
if (slots != null)
{
foreach (var slot in slots)
{
if (slot.item != null && slot.item.id == itemId)
{
count += slot.amount;
}
}
}
if (hotbarSlots != null)
{
foreach (var slot in hotbarSlots)
{
if (slot.item != null && slot.item.id == itemId)
{
count += slot.amount;
}
}
}
return count;
}
/// <summary>
/// Есть ли предмет в инвентаре
/// </summary>
public bool HasItem(string itemId, int amount = 1)
{
return GetItemCount(itemId) >= amount;
}
/// <summary>
/// Получить слот по индексу
/// </summary>
public Slot GetSlot(int index)
{
if (index >= 0 && index < slots.Length)
return slots[index];
return null;
}
/// <summary>
/// Найти первый слот с указанным предметом
/// </summary>
public Slot FindSlotWithItem(string itemId)
{
foreach (var slot in slots)
{
if (slot.item != null && slot.item.id == itemId)
return slot;
}
return null;
}
/// <summary>
/// Найти все слоты с указанным предметом
/// </summary>
public List<Slot> FindAllSlotsWithItem(string itemId)
{
var result = new List<Slot>();
foreach (var slot in slots)
{
if (slot.item != null && slot.item.id == itemId)
result.Add(slot);
}
return result;
}
/// <summary>
/// Найти первый пустой слот
/// </summary>
public Slot FindEmptySlot()
{
foreach (var slot in slots)
{
if (slot.IsEmpty)
return slot;
}
return null;
}
/// <summary>
/// Поменять предметы между двумя слотами
/// </summary>
public void SwapSlots(int fromIndex, int toIndex)
{
if (fromIndex < 0 || fromIndex >= slots.Length ||
toIndex < 0 || toIndex >= slots.Length)
return;
var fromSlot = slots[fromIndex];
var toSlot = slots[toIndex];
// Сохраняем данные
var tempItem = fromSlot.item;
var tempAmount = fromSlot.amount;
// Меняем
if (toSlot.IsEmpty)
{
fromSlot.Clear();
toSlot.SetItem(tempItem, tempAmount);
}
else
{
fromSlot.SetItem(toSlot.item, toSlot.amount);
toSlot.SetItem(tempItem, tempAmount);
}
}
/// <summary>
/// Очистить инвентарь
/// </summary>
public void ClearInventory()
{
foreach (var slot in slots)
{
slot?.Clear();
}
}
/// <summary>
/// Вызывается когда слот очищен (внутренний метод)
/// </summary>
public void OnSlotCleared(int slotIndex)
{
// Можно добавить дополнительную логику
}
// ==================== ТЕСТОВЫЕ МЕТОДЫ ====================
/// <summary>
/// Создать тестовый предмет в мире (для отладки)
/// </summary>
[ContextMenu("Spawn Test Item")]
public void SpawnTestItem()
{
if (itemDatabase == null || itemDatabase.Count == 0)
{
Debug.LogWarning("No items in database!");
return;
}
// Создаём предмет напротив игрока
Vector3 spawnPos = Vector3.zero;
var player = GameObject.FindGameObjectWithTag("Player");
if (player != null)
{
spawnPos = player.transform.position + player.transform.forward * 2f;
spawnPos.y = 0.5f;
}
var item = itemDatabase[0];
if (InventoryItemFactory.Instance != null)
{
InventoryItemFactory.Instance.CreateWorldItem(item, 5, spawnPos);
Debug.Log($"Spawned test item: {item.itemName}");
}
}
// ==================== РАБОТА С JSON ====================
public string ExportToJson()
{
var inventoryData = new InventorySaveData();
inventoryData.slots = new SlotData[slots.Length];
for (int i = 0; i < slots.Length; i++)
{
inventoryData.slots[i] = new SlotData
{
itemId = slots[i].item?.id,
amount = slots[i].amount
};
}
return JsonUtility.ToJson(inventoryData, true);
}
public void ImportFromJson(string json)
{
var inventoryData = JsonUtility.FromJson<InventorySaveData>(json);
for (int i = 0; i < Mathf.Min(inventoryData.slots.Length, slots.Length); i++)
{
var slotData = inventoryData.slots[i];
if (!string.IsNullOrEmpty(slotData.itemId) && _itemDict.TryGetValue(slotData.itemId, out ItemData item))
{
slots[i].SetItem(item, slotData.amount);
}
else
{
slots[i].Clear();
}
}
}
[System.Serializable]
class InventorySaveData
{
public SlotData[] slots;
}
[System.Serializable]
class SlotData
{
public string itemId;
public int amount;
}
}