266 lines
7.1 KiB
C#
266 lines
7.1 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using Sandbox;
|
||
|
||
namespace Sasalka;
|
||
|
||
public class Inventar : Component
|
||
{
|
||
[Flags]
|
||
public enum InventorySlot
|
||
{
|
||
None = 0,
|
||
LeftHand = 1 << 0, // 1
|
||
RightHand = 1 << 1, // 2
|
||
Head = 1 << 2, // 4
|
||
Body = 1 << 3, // 8
|
||
Hands = 1 << 4, // 16
|
||
Bottom = 1 << 5, // 32
|
||
Feet = 1 << 6 // 64
|
||
}
|
||
|
||
[Sync] public IList<InventoryItem> Items { get; set; } = new List<InventoryItem>();
|
||
|
||
[Sync]
|
||
public IDictionary<InventorySlot, InventoryItem> EquippedItems { get; set; } =
|
||
new Dictionary<InventorySlot, InventoryItem>();
|
||
|
||
// Настройка вместимости инвентаря
|
||
[Property] public int MaxInventorySlots { get; set; } = 20; // Максимальное количество слотов
|
||
[Property] public bool UnlimitedSlots { get; set; } = false; // Безлимитный инвентарь
|
||
|
||
public static bool IsInventoryOpen = false;
|
||
|
||
public event Action OnChanged;
|
||
public event Action<InventoryItem> OnEquipped;
|
||
public event Action<InventoryItem> OnUnEquipped;
|
||
public event Action<InventoryItem> OnItemAdded;
|
||
public event Action<InventoryItem> OnItemRemoved;
|
||
|
||
public bool CanAddItem( InventoryItem item )
|
||
{
|
||
if ( item == null || item.Definition == null )
|
||
return false;
|
||
|
||
// Проверяем, есть ли уже такой предмет в инвентаре
|
||
var existingItem = Items.FirstOrDefault( x => x.Definition == item.Definition );
|
||
|
||
if ( existingItem != null )
|
||
{
|
||
// Если предмет уже есть, проверяем, можно ли добавить к нему количество
|
||
return existingItem.Count + item.Count <= item.Definition.MaxCount;
|
||
}
|
||
|
||
// Если предмета нет, проверяем вместимость инвентаря
|
||
if ( !UnlimitedSlots && Items.Count >= MaxInventorySlots )
|
||
{
|
||
return false; // Инвентарь полон
|
||
}
|
||
|
||
// Проверяем только MaxCount для нового предмета
|
||
return item.Count <= item.Definition.MaxCount;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Добавляет предмет в инвентарь, распределяя по существующим и новым стекам. Возвращает остаток, который не удалось добавить (или 0, если всё добавлено).
|
||
/// </summary>
|
||
public int AddItem( InventoryItem item )
|
||
{
|
||
if ( item == null || item.Definition == null || item.Count <= 0 )
|
||
return item.Count;
|
||
|
||
int toAdd = item.Count;
|
||
|
||
// 1. Заполняем существующие стаки
|
||
foreach ( var stack in Items.Where( x => x.Definition == item.Definition && x.Count < x.Definition.MaxCount ) )
|
||
{
|
||
int canAdd = Math.Min( toAdd, stack.Definition.MaxCount - stack.Count );
|
||
if ( canAdd > 0 )
|
||
{
|
||
stack.Count += canAdd;
|
||
toAdd -= canAdd;
|
||
OnChanged?.Invoke();
|
||
OnItemAdded?.Invoke( stack );
|
||
}
|
||
|
||
if ( toAdd <= 0 ) return 0;
|
||
}
|
||
|
||
// 2. Добавляем новые стаки, если есть место
|
||
while ( toAdd > 0 && (UnlimitedSlots || Items.Count < MaxInventorySlots) )
|
||
{
|
||
int stackCount = Math.Min( toAdd, item.Definition.MaxCount );
|
||
var newStack = new InventoryItem { Definition = item.Definition, Count = stackCount };
|
||
Items.Add( newStack );
|
||
toAdd -= stackCount;
|
||
OnChanged?.Invoke();
|
||
OnItemAdded?.Invoke( newStack );
|
||
}
|
||
|
||
// 3. Возвращаем остаток, если не всё удалось добавить
|
||
return toAdd;
|
||
}
|
||
|
||
public bool RemoveItem( InventoryItem item, int count = 1 )
|
||
{
|
||
if ( item == null || !Items.Contains( item ) )
|
||
return false;
|
||
|
||
if ( count >= item.Count )
|
||
{
|
||
// Удаляем весь предмет
|
||
UnEquipItem( item );
|
||
Items.Remove( item );
|
||
OnChanged?.Invoke();
|
||
OnItemRemoved?.Invoke( item );
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
// Уменьшаем количество
|
||
item.Count -= count;
|
||
OnChanged?.Invoke();
|
||
return true;
|
||
}
|
||
}
|
||
|
||
public bool EquipItem( InventoryItem item )
|
||
{
|
||
if ( item?.Definition is not IEquipable equipable )
|
||
return false;
|
||
|
||
var slot = item.Definition.GetSlot();
|
||
if ( slot == null )
|
||
return false;
|
||
|
||
// Если уже экипирован этот же предмет — снять его
|
||
if ( EquippedItems.Values.Contains( item ) )
|
||
{
|
||
UnEquipItem( item );
|
||
return true;
|
||
}
|
||
|
||
// Если на этом слоте уже что-то есть — снять старый предмет
|
||
if ( EquippedItems.TryGetValue( slot.Value, out var oldItem ) )
|
||
{
|
||
UnEquipItem( oldItem );
|
||
}
|
||
|
||
// Экипировать новый предмет
|
||
EquippedItems[slot.Value] = item;
|
||
item.Equipped = true;
|
||
item.OnEquipped();
|
||
OnEquipped?.Invoke( item );
|
||
OnChanged?.Invoke();
|
||
return true;
|
||
}
|
||
|
||
public void DropItem( InventoryItem item, Vector3 position )
|
||
{
|
||
if ( item == null || !Items.Contains( item ) )
|
||
return;
|
||
|
||
// // Создаем копию предмета для выбрасывания
|
||
// var droppedItem = new InventoryItem
|
||
// {
|
||
// Definition = item.Definition,
|
||
// Count = item.Count // Выбрасываем всю стопку
|
||
// };
|
||
|
||
GameObject gO = item.Definition.Prefab.Clone( position );
|
||
|
||
if ( gO.Components.TryGet<InventoryItem>( out var inventoryItem ) )
|
||
{
|
||
inventoryItem.Count = item.Count;
|
||
inventoryItem.Definition = item.Definition;
|
||
}
|
||
|
||
gO.NetworkSpawn();
|
||
|
||
// Удаляем весь предмет из инвентаря
|
||
RemoveItem( item, item.Count );
|
||
}
|
||
|
||
public void UnEquipItem( InventoryItem item )
|
||
{
|
||
if ( item == null )
|
||
return;
|
||
|
||
var slotToRemove = EquippedItems.FirstOrDefault( kvp => kvp.Value == item ).Key;
|
||
|
||
if ( EquippedItems.ContainsKey( slotToRemove ) )
|
||
{
|
||
EquippedItems.Remove( slotToRemove );
|
||
item.Equipped = false;
|
||
item.OnUnEquipped();
|
||
OnUnEquipped?.Invoke( item );
|
||
OnChanged?.Invoke();
|
||
}
|
||
}
|
||
|
||
public InventoryItem GetEquippedItem( InventorySlot slot )
|
||
{
|
||
return EquippedItems.TryGetValue( slot, out var item ) ? item : null;
|
||
}
|
||
|
||
public bool IsSlotOccupied( InventorySlot slot )
|
||
{
|
||
return EquippedItems.ContainsKey( slot );
|
||
}
|
||
|
||
public void ClearInventory()
|
||
{
|
||
// Снимаем все экипированные предметы
|
||
foreach ( var item in EquippedItems.Values.ToList() )
|
||
{
|
||
UnEquipItem( item );
|
||
}
|
||
|
||
Items.Clear();
|
||
OnChanged?.Invoke();
|
||
}
|
||
|
||
// Публичный метод для уведомления об изменениях извне класса
|
||
public void NotifyChanged()
|
||
{
|
||
OnChanged?.Invoke();
|
||
}
|
||
|
||
// Публичный метод для уведомления о добавлении предмета извне класса
|
||
public void NotifyItemAdded( InventoryItem item )
|
||
{
|
||
OnItemAdded?.Invoke( item );
|
||
}
|
||
|
||
// Методы для получения информации о вместимости
|
||
public int GetUsedSlots()
|
||
{
|
||
return Items.Count;
|
||
}
|
||
|
||
public int GetAvailableSlots()
|
||
{
|
||
if ( UnlimitedSlots )
|
||
return int.MaxValue;
|
||
|
||
return Math.Max( 0, MaxInventorySlots - Items.Count );
|
||
}
|
||
|
||
public bool IsInventoryFull()
|
||
{
|
||
if ( UnlimitedSlots )
|
||
return false;
|
||
|
||
return Items.Count >= MaxInventorySlots;
|
||
}
|
||
|
||
public float GetInventoryUsagePercentage()
|
||
{
|
||
if ( UnlimitedSlots )
|
||
return 0f;
|
||
|
||
return (float)Items.Count / MaxInventorySlots * 100f;
|
||
}
|
||
}
|