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> _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 GetAllPlayerInventoryItems() { var list = new List(); _lock.EnterReadLock(); try { foreach (KeyValuePair> 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 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 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 GetItemsByInventoryType(InventoryType type) { if (type == InventoryType.EquippedItems) { return Array.Empty(); } _lock.EnterReadLock(); try { if (_inventoryItemsByInventoryType.TryGetValue(type, out List items)) { return items; } return Array.Empty(); } finally { _lock.ExitReadLock(); } } public IEnumerable 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 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 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 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 item)) { item = new List(); _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 inventoryItems)) { removedItem = null; return false; } InventoryItem item = inventoryItems.FirstOrDefault(x => x?.Slot == slot); if (item == null) { removedItem = null; return false; } List 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 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 item)) { return null; } return item.FirstOrDefault(x => x?.Slot == slot); } finally { _lock.ExitReadLock(); } } }