using System; using System.Collections.Generic; using UnityEngine; namespace BrewMonster.Scripts { /// /// C# mirror of C++ CECInventory (EC_Inventory.h / EC_Inventory.cpp). /// Instance-based inventory core plus static helpers used by existing client code. /// public class EC_Inventory { // ===== Instance-based inventory (per-pack) ===== public static int IVTRTYPE_CLIENT_GENERALCARD_PACK = 1024; // �ͻ��˱��ذ����� ���ڿ���ͼ��Ҫ����ѻ�ÿ���ͨ�����췢�͡�Ϊ��ͳһ�������촰�ڵ���Ʒ�����ӱ��ذ����� // Item array: index is slot, null means empty. private EC_IvtrItem[] m_aItems = Array.Empty(); public static class Inventory_type { public const int IVTRTYPE_PACK = 0, // Normal pack IVTRTYPE_EQUIPPACK = 1, // Equipment IVTRTYPE_TASKPACK = 2, // Task pack IVTRTYPE_TRASHBOX = 3, // Trash box IVTRTYPE_TRASHBOX2 = 4, // Trash box - material box IVTRTYPE_TRASHBOX3 = 5, // Trash box - fashion box IVTRTYPE_ACCOUNT_BOX = 6, // User account box IVTRTYPE_GENERALCARD_BOX = 7; // ���ư��� }; public EC_Inventory() { } public bool Init(int iSize) { Resize(iSize); return true; } public void Release() { // In C++ this deletes all heap-allocated items. // Here we simply clear references so GC can collect them. RemoveAllItems(); m_aItems = Array.Empty(); } public void RemoveAllItems() { for (int i = 0; i < m_aItems.Length; i++) { m_aItems[i] = null; } } public virtual void Resize(int iNewSize) { int oldSize = m_aItems.Length; if (iNewSize < 0) iNewSize = 0; if (iNewSize == oldSize) return; var newArray = iNewSize > 0 ? new EC_IvtrItem[iNewSize] : Array.Empty(); if (oldSize > 0 && iNewSize > 0) { Array.Copy(m_aItems, newArray, Math.Min(oldSize, iNewSize)); } m_aItems = newArray; } public EC_IvtrItem PutItem(int iSlot, EC_IvtrItem pItem) { if (iSlot < 0 || iSlot >= m_aItems.Length) { return null; } var old = m_aItems[iSlot]; m_aItems[iSlot] = pItem; EventBus.Publish(new InventoryChangedEvent()); return old; } public void SetItem(int iSlot, EC_IvtrItem pItem) { if (iSlot < 0 || iSlot >= m_aItems.Length) { return; } m_aItems[iSlot] = pItem; EventBus.Publish(new InventoryChangedEvent()); } public EC_IvtrItem GetItem(int iSlot, bool bRemove = false) { if (iSlot < 0 || iSlot >= m_aItems.Length) { return null; } var pItem = m_aItems[iSlot]; if (bRemove) m_aItems[iSlot] = null; return pItem; } public void ExchangeItem(int iSlot1, int iSlot2) { if (iSlot1 < 0 || iSlot1 >= m_aItems.Length || iSlot2 < 0 || iSlot2 >= m_aItems.Length) { return; } if (iSlot1 == iSlot2) return; var tmp = m_aItems[iSlot1]; m_aItems[iSlot1] = m_aItems[iSlot2]; m_aItems[iSlot2] = tmp; } /// /// Place or stack item in a specific slot (server-specified slot). Matches C++ expectation that client uses same slot as server. /// public bool PutItemInSlot(int iSlot, int tid, int iExpireDate, int iAmount, out int piLastSlot, out int piLastAmount) { piLastSlot = -1; piLastAmount = 0; if (iSlot < 0 || iSlot >= m_aItems.Length || iAmount <= 0) return false; var slotItem = m_aItems[iSlot]; if (slotItem == null) { var newItem = EC_IvtrItem.CreateItem(tid, iExpireDate, iAmount); if (newItem == null) return false; newItem.Slot = iSlot; newItem.SetCount(iAmount); m_aItems[iSlot] = newItem; piLastSlot = iSlot; piLastAmount = iAmount; EventBus.Publish(new InventoryChangedEvent()); return true; } if (slotItem.GetTemplateID() != tid) return false; int pileLimit = Math.Max(1, EC_IvtrItem.GetPileLimit(tid)); int canAdd = Math.Max(0, pileLimit - Math.Max(0, slotItem.GetCount())); if (canAdd <= 0) return false; int add = Math.Min(canAdd, iAmount); slotItem.AddAmount(add); piLastSlot = iSlot; piLastAmount = slotItem.GetCount(); EventBus.Publish(new InventoryChangedEvent()); return true; } /// C++ CECInventory::MergeItem: walk slots, call CECIvtrItem::MergeItem, then new slot if needed. public bool MergeItem(int tid, int iExpireDate, int iAmount, out int piLastSlot, out int piLastAmount) { piLastSlot = -1; piLastAmount = 0; int firstEmpty = -1; for (int i = 0; i < m_aItems.Length; i++) { var slotItem = m_aItems[i]; if (slotItem != null) { int iNumMerge = slotItem.MergeItem(tid, iAmount); iAmount -= iNumMerge; if (iAmount == 0) { piLastSlot = i; piLastAmount = slotItem.GetCount(); EventBus.Publish(new InventoryChangedEvent()); return true; } } else if (firstEmpty < 0) { firstEmpty = i; } } if (firstEmpty < 0 || iAmount <= 0) { return false; } var newItem = EC_IvtrItem.CreateItem(tid, iExpireDate, iAmount); if (newItem == null) return false; newItem.Slot = firstEmpty; newItem.SetCount(iAmount); m_aItems[firstEmpty] = newItem; piLastSlot = firstEmpty; piLastAmount = iAmount; EventBus.Publish(new InventoryChangedEvent()); return true; } public bool MoveItem(int iSrc, int iDest, int iAmount) { if (iSrc < 0 || iSrc >= m_aItems.Length || iDest < 0 || iDest >= m_aItems.Length) { return false; } var pSrc = m_aItems[iSrc]; var pDst = m_aItems[iDest]; if (pSrc == null) return false; if (iAmount == 0) return false; if (pDst == null) { var clone = EC_IvtrItem.CreateItem(pSrc.GetTemplateID(), pSrc.GetExpireDate(), iAmount); m_aItems[iDest] = clone; } else { if (pSrc.GetTemplateID() != pDst.GetTemplateID()) return false; int pileLimit = Math.Max(1, EC_IvtrItem.GetPileLimit(pDst.GetTemplateID())); int canAdd = Math.Max(0, pileLimit - Math.Max(0, pDst.GetCount())); int add = Math.Min(canAdd, iAmount); if (add <= 0) return false; pDst.AddAmount(add); iAmount = add; } RemoveItem(iSrc, iAmount); EventBus.Publish(new InventoryChangedEvent()); return true; } public bool RemoveItem(int iSlot, int iAmount) { if (iSlot < 0 || iSlot >= m_aItems.Length) { return false; } var pItem = m_aItems[iSlot]; if (pItem == null) return true; int newCount = pItem.AddAmount(-Math.Max(0, iAmount)); if (newCount <= 0) m_aItems[iSlot] = null; EventBus.Publish(new InventoryChangedEvent()); return true; } public int FindItem(int idItem, int baseIdx = 0) { if (baseIdx < 0) baseIdx = 0; for (int i = baseIdx; i < m_aItems.Length; i++) { var pItem = m_aItems[i]; if (pItem != null && pItem.GetTemplateID() == idItem) return i; } return -1; } public int GetItemTotalNum(int idItem) { int count = 0; for (int i = 0; i < m_aItems.Length; i++) { var pItem = m_aItems[i]; if (pItem != null && pItem.GetTemplateID() == idItem) count += Math.Max(0, pItem.GetCount()); } return count; } public int SearchEmpty() { for (int i = 0; i < m_aItems.Length; i++) { if (m_aItems[i] == null) return i; } return -1; } public int GetEmptySlotNum() { int count = 0; for (int i = 0; i < m_aItems.Length; i++) { if (m_aItems[i] == null) count++; } return count; } public int CanAddItem(int idItem, int iAmount, bool tryPile) { int foundEmpty = -1; for (int i = 0; i < m_aItems.Length; i++) { var pItem = m_aItems[i]; if (pItem == null) { if (!tryPile) return i; if (foundEmpty < 0) foundEmpty = i; } else if (pItem.GetTemplateID() == idItem) { int pileLimit = Math.Max(1, EC_IvtrItem.GetPileLimit(idItem)); if (pItem.GetCount() + iAmount <= pileLimit) return i; } } return foundEmpty; } public int GetSize() { return m_aItems.Length; } // Unfreeze all items public void UnfreezeAllItems() { // Release all items for (int i=0; i < m_aItems.Length; i++) { if (m_aItems[i] != null) m_aItems[i].Freeze(false); } } public void GetAllItemsOfType(out List items) where T : EC_IvtrItem { items = new List(); for (int i = 0; i < m_aItems.Length; i++) { if (m_aItems[i] != null) if (m_aItems[i] is T item) items.Add(item); } } } /// /// Fired when any client-side inventory pack mutates (add/remove/move/merge). /// Used by UI (quickbar HP/MP auto-pick) to re-evaluate best potions. /// public struct InventoryChangedEvent { } }