server-master/srcs/WingsAPI.Game/Inventory/InventoryComponent.cs
2026-02-10 18:21:30 +01:00

432 lines
No EOL
13 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using WingsEmu.Core.Extensions;
using WingsEmu.Game.Characters;
using WingsEmu.Game.Extensions;
using WingsEmu.Game.Items;
using WingsEmu.Game.Managers.StaticData;
using WingsEmu.Packets.Enums;
namespace WingsEmu.Game.Inventory;
public class InventoryComponent : IInventoryComponent
{
private const int WEAR_SLOTS = 17;
private readonly IPlayerEntity _character;
private readonly Dictionary<InventoryType, List<InventoryItem>> _inventoryItemsByInventoryType = new();
private readonly IItemsManager _itemsManager;
private readonly ReaderWriterLockSlim _lock = new();
private readonly InventoryItem[] _wear = new InventoryItem[WEAR_SLOTS];
public InventoryComponent(IPlayerEntity character, IItemsManager itemsManager)
{
_character = character;
_itemsManager = itemsManager;
}
public IEnumerable<InventoryItem> GetAllPlayerInventoryItems()
{
var list = new List<InventoryItem>();
_lock.EnterReadLock();
try
{
foreach (KeyValuePair<InventoryType, List<InventoryItem>> keyValuePair in _inventoryItemsByInventoryType)
{
list.AddRange(keyValuePair.Value);
}
foreach (InventoryItem inventoryItem in _wear)
{
if (inventoryItem == null)
{
continue;
}
list.Add(inventoryItem);
}
return list;
}
finally
{
_lock.ExitReadLock();
}
}
public InventoryItem GetFirstItemByVnum(int vnum)
{
IGameItem gameItem = _itemsManager.GetItem(vnum);
if (gameItem == null)
{
return null;
}
_lock.EnterReadLock();
try
{
if (!_inventoryItemsByInventoryType.TryGetValue(gameItem.Type, out List<InventoryItem> items))
{
return null;
}
return items.Where(s => s?.ItemInstance != null && s.ItemInstance.ItemVNum == vnum).OrderBy(x => x.Slot).FirstOrDefault();
}
finally
{
_lock.ExitReadLock();
}
}
public InventoryItem FindItemWithoutFullStack(int vnum, short amount)
{
IGameItem gameItem = _itemsManager.GetItem(vnum);
if (gameItem == null)
{
return null;
}
_lock.EnterReadLock();
try
{
if (!_inventoryItemsByInventoryType.TryGetValue(gameItem.Type, out List<InventoryItem> items))
{
return null;
}
return items.Where(s => s?.ItemInstance != null && s.ItemInstance.ItemVNum == vnum && s.ItemInstance.Amount + amount <= 999).OrderBy(x => x.Slot).FirstOrDefault();
}
finally
{
_lock.ExitReadLock();
}
}
public IEnumerable<InventoryItem> GetItemsByInventoryType(InventoryType type)
{
if (type == InventoryType.EquippedItems)
{
return Array.Empty<InventoryItem>();
}
_lock.EnterReadLock();
try
{
if (_inventoryItemsByInventoryType.TryGetValue(type, out List<InventoryItem> items))
{
return items;
}
return Array.Empty<InventoryItem>();
}
finally
{
_lock.ExitReadLock();
}
}
public IEnumerable<InventoryItem> EquippedItems => _wear;
public GameItemInstance GetItemInstanceFromEquipmentSlot(EquipmentType type) => _wear[(byte)type]?.ItemInstance;
public GameItemInstance MainWeapon => _wear[(byte)EquipmentType.MainWeapon]?.ItemInstance;
public GameItemInstance SecondaryWeapon => _wear[(byte)EquipmentType.SecondaryWeapon]?.ItemInstance;
public GameItemInstance Armor => _wear[(byte)EquipmentType.Armor]?.ItemInstance;
public GameItemInstance Hat => _wear[(byte)EquipmentType.Hat]?.ItemInstance;
public GameItemInstance Amulet => _wear[(byte)EquipmentType.Amulet]?.ItemInstance;
public GameItemInstance Gloves => _wear[(byte)EquipmentType.Gloves]?.ItemInstance;
public GameItemInstance Ring => _wear[(byte)EquipmentType.Ring]?.ItemInstance;
public GameItemInstance Necklace => _wear[(byte)EquipmentType.Necklace]?.ItemInstance;
public GameItemInstance Bracelet => _wear[(byte)EquipmentType.Bracelet]?.ItemInstance;
public GameItemInstance Boots => _wear[(byte)EquipmentType.Boots]?.ItemInstance;
public GameItemInstance Fairy => _wear[(byte)EquipmentType.Fairy]?.ItemInstance;
public GameItemInstance Mask => _wear[(byte)EquipmentType.Mask]?.ItemInstance;
public GameItemInstance CostumeSuit => _wear[(byte)EquipmentType.CostumeSuit]?.ItemInstance;
public GameItemInstance CostumeHat => _wear[(byte)EquipmentType.CostumeHat]?.ItemInstance;
public GameItemInstance WeaponSkin => _wear[(byte)EquipmentType.WeaponSkin]?.ItemInstance;
public GameItemInstance Wings => _wear[(byte)EquipmentType.Wings]?.ItemInstance;
public GameItemInstance Specialist => _wear[(byte)EquipmentType.Sp]?.ItemInstance;
public bool InventoryIsInitialized { get; } = false;
public int CountItemWithVnum(int vnum)
{
IGameItem gameItem = _itemsManager.GetItem(vnum);
if (gameItem == null)
{
return default;
}
_lock.EnterReadLock();
try
{
if (!_inventoryItemsByInventoryType.TryGetValue(gameItem.Type, out List<InventoryItem> items))
{
return 0;
}
return items.Where(s => s?.ItemInstance != null && s.ItemInstance.ItemVNum == vnum).Sum(s => s.ItemInstance.Amount);
}
finally
{
_lock.ExitReadLock();
}
}
public bool HasSpaceFor(int vnum, short amount = 1)
{
IGameItem gameItem = _itemsManager.GetItem(vnum);
if (gameItem == null)
{
return false;
}
_lock.EnterReadLock();
try
{
if (!_inventoryItemsByInventoryType.TryGetValue(gameItem.Type, out List<InventoryItem> items))
{
// no inventory in that InventoryType
return true;
}
if (items == null || !items.Any())
{
return true;
}
// Find first free inventory slot - previous version was looking for by lists index, but now is by item slot
short slots = _character.GetInventorySlots(false, gameItem.Type);
for (short i = 0; i < slots; i++)
{
InventoryItem freeSlot = items.FirstOrDefault(x => x != null && x.Slot == i);
if (freeSlot == null)
{
return true;
}
if (freeSlot.ItemInstance == null)
{
continue;
}
if (freeSlot.ItemInstance.GameItem.Id != vnum)
{
continue;
}
if (freeSlot.ItemInstance.GameItem.IsNotStackableInventoryType() || freeSlot.ItemInstance.Amount + amount > 999)
{
continue;
}
return true;
}
return items.Count(s => s?.ItemInstance != null) <= _character.GetInventorySlots(true, gameItem.Type);
}
finally
{
_lock.ExitReadLock();
}
}
public bool HasItem(int vnum, short amount = 1)
{
IGameItem gameItem = _itemsManager.GetItem(vnum);
if (gameItem == null)
{
return false;
}
_lock.EnterReadLock();
try
{
if (!_inventoryItemsByInventoryType.TryGetValue(gameItem.Type, out List<InventoryItem> items))
{
return false;
}
return items.Where(s => s?.ItemInstance != null && s.ItemInstance.ItemVNum == vnum).Sum(s => s.ItemInstance.Amount) >= amount;
}
finally
{
_lock.ExitReadLock();
}
}
public bool RemoveItemAmountByVnum(int vnum, short amount, out InventoryItem removedItem)
{
IGameItem gameItem = _itemsManager.GetItem(vnum);
if (gameItem == null)
{
removedItem = null;
return false;
}
_lock.EnterWriteLock();
try
{
InventoryItem getItem = GetFirstItemByVnum(vnum);
if (getItem == null)
{
removedItem = null;
return false;
}
getItem.ItemInstance.Amount -= amount;
removedItem = getItem;
return true;
}
finally
{
_lock.ExitWriteLock();
}
}
public void EquipItem(InventoryItem item, EquipmentType type, bool force = false)
{
if (force == false)
{
RemoveItemFromSlotAndType(item.Slot, item.InventoryType, out InventoryItem _);
}
item.Slot = (short)item.ItemInstance.GameItem.EquipmentSlot;
item.InventoryType = InventoryType.EquippedItems;
item.IsEquipped = true;
_wear[(byte)type] = item;
}
public void TakeOffItem(EquipmentType type, short? slot = null, InventoryType? inventoryType = null)
{
InventoryItem item = _wear[(byte)type];
if (item == null)
{
return;
}
InventoryType invType = inventoryType ?? InventoryType.Equipment;
item.InventoryType = invType;
item.IsEquipped = false;
item.Slot = slot ?? _character.GetNextInventorySlot(invType);
AddItemToInventory(item);
_wear[(byte)type] = null;
}
public void AddItemToInventory(InventoryItem inventoryItem)
{
InventoryType inventoryType = inventoryItem.InventoryType;
if (inventoryType == InventoryType.EquippedItems)
{
return;
}
_lock.EnterReadLock();
try
{
if (!_inventoryItemsByInventoryType.TryGetValue(inventoryType, out List<InventoryItem> item))
{
item = new List<InventoryItem>();
_inventoryItemsByInventoryType[inventoryType] = item;
}
item.Add(inventoryItem);
}
finally
{
_lock.ExitReadLock();
}
}
public bool RemoveItemFromSlotAndType(short slot, InventoryType type, out InventoryItem removedItem)
{
if (type == InventoryType.EquippedItems)
{
removedItem = null;
return false;
}
_lock.EnterReadLock();
try
{
if (!_inventoryItemsByInventoryType.TryGetValue(type, out List<InventoryItem> inventoryItems))
{
removedItem = null;
return false;
}
InventoryItem item = inventoryItems.FirstOrDefault(x => x?.Slot == slot);
if (item == null)
{
removedItem = null;
return false;
}
List<InventoryItem> items = _inventoryItemsByInventoryType.GetOrDefault(type);
if (items == null)
{
removedItem = null;
return false;
}
items.Remove(item);
removedItem = item;
return true;
}
finally
{
_lock.ExitReadLock();
}
}
public InventoryItem GetInventoryItemFromEquipmentSlot(EquipmentType type) => _wear[(byte)type] == null ? null : _wear[(byte)type];
public InventoryItem GetItemBySlotAndType(short slot, InventoryType type)
{
if (type == InventoryType.EquippedItems)
{
return null;
}
_lock.EnterReadLock();
try
{
if (!_inventoryItemsByInventoryType.TryGetValue(type, out List<InventoryItem> item))
{
return null;
}
return item.FirstOrDefault(x => x?.Slot == slot);
}
finally
{
_lock.ExitReadLock();
}
}
public InventoryItem GetItemBySlotAndType(short slot, InventoryType type, bool force)
{
if (type == InventoryType.EquippedItems && force == false)
{
return null;
}
_lock.EnterReadLock();
try
{
if (!_inventoryItemsByInventoryType.TryGetValue(type, out List<InventoryItem> item))
{
return null;
}
return item.FirstOrDefault(x => x?.Slot == slot);
}
finally
{
_lock.ExitReadLock();
}
}
}