diff --git a/Assets/PerfectWorld/Scripts/Inventory/EC_IvtrType.cs b/Assets/PerfectWorld/Scripts/Inventory/EC_IvtrType.cs
index c42244873d..a1d0ea8fe1 100644
--- a/Assets/PerfectWorld/Scripts/Inventory/EC_IvtrType.cs
+++ b/Assets/PerfectWorld/Scripts/Inventory/EC_IvtrType.cs
@@ -61,5 +61,10 @@ namespace BrewMonster.Scripts
public const int IVTRSIZE_CLIENTCARDPACK = 32; // Client pack for general card collection
public const int NUM_NPCIVTR = 8; // NPC inventory number
+
+ // Inventory package types (match legacy C++ IVTRTYPE_*)
+ public const byte IVTRTYPE_PACK = 0;
+ public const byte IVTRTYPE_EQUIPPACK = 1;
+ public const byte IVTRTYPE_TASKPACK = 2;
}
}
\ No newline at end of file
diff --git a/Assets/PerfectWorld/Scripts/Managers/CECInventory.cs b/Assets/PerfectWorld/Scripts/Managers/CECInventory.cs
deleted file mode 100644
index 107493f88f..0000000000
--- a/Assets/PerfectWorld/Scripts/Managers/CECInventory.cs
+++ /dev/null
@@ -1,798 +0,0 @@
-using System;
-using System.Collections.Generic;
-using UnityEngine;
-
-namespace BrewMonster.Scripts.Managers
-{
- ///
- /// C# mirror of C++ CECInventory (EC_Inventory.h / EC_Inventory.cpp).
- /// This is an instance-based inventory using a fixed-size item array.
- ///
- public class CECInventory
- {
- // Item array: index is slot, null means empty.
- private EC_IvtrItem[] m_aItems = Array.Empty();
-
- public CECInventory()
- {
- }
-
- 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 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;
- return old;
- }
-
- public void SetItem(int iSlot, EC_IvtrItem pItem)
- {
- if (iSlot < 0 || iSlot >= m_aItems.Length)
- {
- return;
- }
-
- m_aItems[iSlot] = pItem;
- }
-
- 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;
- }
-
- 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)
- {
- if (slotItem.GetTemplateID() != tid)
- continue;
-
- int pileLimit = Math.Max(1, EC_IvtrItem.GetPileLimit(tid));
- int canAdd = Math.Max(0, pileLimit - Math.Max(0, slotItem.GetCount()));
- if (canAdd <= 0) continue;
-
- int add = Math.Min(canAdd, iAmount);
- slotItem.AddAmount(add);
- iAmount -= add;
-
- if (iAmount == 0)
- {
- piLastSlot = i;
- piLastAmount = slotItem.GetCount();
- return true;
- }
- }
- else if (firstEmpty < 0)
- {
- firstEmpty = i;
- }
- }
-
- if (firstEmpty < 0 || iAmount <= 0)
- {
- return false;
- }
-
- var newItem = new EC_IvtrItem(tid, iExpireDate)
- {
- Slot = firstEmpty,
- State = 0,
- Crc = 0,
- Content = null
- };
- newItem.SetCount(iAmount);
-
- m_aItems[firstEmpty] = newItem;
- piLastSlot = firstEmpty;
- piLastAmount = iAmount;
- 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 = new EC_IvtrItem(pSrc.GetTemplateID(), pSrc.GetExpireDate())
- {
- Slot = iDest,
- Package = pSrc.Package,
- State = pSrc.State,
- Crc = pSrc.Crc,
- Content = pSrc.Content != null ? (byte[])pSrc.Content.Clone() : null
- };
- clone.SetCount(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);
- 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;
-
- 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;
- }
- }
-
- ///
- /// Static inventory facade used by the current client code.
- /// This implementation was originally in EC_Inventory.cs and is now colocated
- /// with CECInventory so both static-style and instance-style code continue to work.
- ///
- public static class EC_Inventory
- {
- // We currently support exactly three inventory packs, matching legacy C++ semantics:
- // 0 = normal pack, 1 = equip pack, 2 = task pack.
- private const int PackageCount = 3;
-
- // Fixed-size arrays per package, mimicking C++ CECInventory::m_aItems (APtrArray).
- // Index is the slot index; a null entry means the slot is empty.
- private static readonly int[] _packSizeByPackage = new int[PackageCount];
- private static readonly EC_IvtrItem[][] _itemsByPackage = new EC_IvtrItem[PackageCount][];
- private const int MaxContentHexToLog = 64;
-
- // Package constants to mirror legacy C++ names
- public const byte IVTRTYPE_PACK = 0;
- public const byte IVTRTYPE_EQUIPPACK = 1;
- public const byte IVTRTYPE_TASKPACK = 2;
-
- private static int GetPackageIndex(byte pkg)
- {
- switch (pkg)
- {
- case IVTRTYPE_PACK:
- case IVTRTYPE_EQUIPPACK:
- case IVTRTYPE_TASKPACK:
- return pkg;
- default:
- // Only three legacy packages are currently supported; ignore others safely.
- Debug.LogWarning($"[Inventory] Unsupported package id={pkg}, expected 0..2.");
- return -1;
- }
- }
-
- private static int GetPackSize(byte byPackage)
- {
- int idx = GetPackageIndex(byPackage);
- if (idx < 0) return 0;
- return _packSizeByPackage[idx];
- }
-
- private static EC_IvtrItem[] EnsureSlots(byte byPackage)
- {
- int idx = GetPackageIndex(byPackage);
- if (idx < 0)
- return Array.Empty();
-
- int size = Math.Max(0, _packSizeByPackage[idx]);
- var slots = _itemsByPackage[idx];
-
- if (slots == null || slots.Length != size)
- {
- var newSlots = size > 0 ? new EC_IvtrItem[size] : Array.Empty();
-
- // Preserve items that still fit into the resized pack, similar to C++ Resize.
- if (slots != null && slots.Length > 0 && newSlots.Length > 0)
- {
- Array.Copy(slots, newSlots, Math.Min(slots.Length, newSlots.Length));
- }
-
- _itemsByPackage[idx] = newSlots;
- slots = newSlots;
- }
-
- return slots;
- }
-
- private static string GetPackageName(byte pkg)
- {
- switch (pkg)
- {
- case 0: return "IVTRTYPE_PACK";
- case 1: return "IVTRTYPE_EQUIPPACK";
- case 2: return "IVTRTYPE_TASKPACK";
- default: return "PACK_UNKNOWN";
- }
- }
-
- private static string BytesToHex(byte[] bytes, int max)
- {
- if (bytes == null || bytes.Length == 0) return "";
- int len = Math.Min(bytes.Length, max);
- string hex = BitConverter.ToString(bytes, 0, len);
- if (bytes.Length > len) hex += "-...";
- return hex;
- }
-
- // C++ has CECInventory per pack; this helper returns a snapshot Dictionary view
- // of the current slots for convenience where a map-like API is easier to use.
- public static Dictionary GetPack(byte byPackage)
- {
- var slots = EnsureSlots(byPackage);
- var result = new Dictionary(slots.Length);
- for (int i = 0; i < slots.Length; i++)
- {
- var it = slots[i];
- if (it != null)
- result[i] = it;
- }
- return result;
- }
-
- public static EC_IvtrItem GetItem(int iSlot, bool bRemove)
- {
- // Backward-compatible overload defaulting to inventory package
- return GetItem(IVTRTYPE_PACK, iSlot, bRemove);
- }
-
- public static EC_IvtrItem GetItem(byte byPackage, int slot, bool remove)
- {
- var slots = EnsureSlots(byPackage);
- if (slot < 0 || slot >= slots.Length)
- return null;
-
- var item = slots[slot];
- if (remove && slot >= 0 && slot < slots.Length)
- slots[slot] = null;
- return item;
- }
-
- public static void Resize(byte byPackage, int newSize)
- {
- int idx = GetPackageIndex(byPackage);
- if (idx < 0)
- return;
-
- newSize = Math.Max(0, newSize);
- int oldSize = _packSizeByPackage[idx];
- _packSizeByPackage[idx] = newSize;
-
- var oldSlots = _itemsByPackage[idx];
-
- if (oldSlots == null)
- {
- _itemsByPackage[idx] = newSize > 0 ? new EC_IvtrItem[newSize] : Array.Empty();
- return;
- }
-
- if (oldSize == newSize && oldSlots.Length == newSize)
- return;
-
- var newSlots = newSize > 0 ? new EC_IvtrItem[newSize] : Array.Empty();
- if (oldSlots.Length > 0 && newSlots.Length > 0)
- {
- Array.Copy(oldSlots, newSlots, Math.Min(oldSlots.Length, newSlots.Length));
- }
- _itemsByPackage[idx] = newSlots;
- }
-
- public static EC_IvtrItem PutItem(byte byPackage, int slot, EC_IvtrItem item)
- {
- var slots = EnsureSlots(byPackage);
- if (slot < 0 || slot >= slots.Length)
- return null;
-
- var oldItem = slots[slot];
- slots[slot] = item;
- return oldItem;
- }
-
- public static void SetItem(byte byPackage, int slot, EC_IvtrItem item)
- {
- var slots = EnsureSlots(byPackage);
- if (slot < 0 || slot >= slots.Length)
- return;
- slots[slot] = item;
- }
-
- public static void ExchangeItem(byte byPackage, int slot1, int slot2)
- {
- if (slot1 == slot2) return;
- var slots = EnsureSlots(byPackage);
- if (slot1 < 0 || slot1 >= slots.Length || slot2 < 0 || slot2 >= slots.Length)
- return;
-
- var i1 = slots[slot1];
- var i2 = slots[slot2];
- slots[slot1] = i2;
- slots[slot2] = i1;
- }
-
- public static bool RemoveItem(byte byPackage, int slot, int amount)
- {
- var slots = EnsureSlots(byPackage);
- if (slot < 0 || slot >= slots.Length)
- return false;
-
- var item = slots[slot];
- if (item == null)
- return true;
-
- int newCount = Math.Max(0, item.m_iCount - Math.Max(0, amount));
- item.m_iCount = newCount;
- if (newCount <= 0)
- slots[slot] = null;
-
- return true;
- }
-
- public static void RemoveAllItems(byte byPackage)
- {
- var slots = EnsureSlots(byPackage);
- for (int i = 0; i < slots.Length; i++)
- {
- slots[i] = null;
- }
- }
-
- public static int SearchEmpty(byte byPackage)
- {
- var slots = EnsureSlots(byPackage);
- for (int i = 0; i < slots.Length; i++)
- {
- if (slots[i] == null)
- return i;
- }
- return -1;
- }
-
- public static int GetEmptySlotNum(byte byPackage)
- {
- var slots = EnsureSlots(byPackage);
- int empty = 0;
- for (int i = 0; i < slots.Length; i++)
- {
- if (slots[i] == null)
- empty++;
- }
- return empty;
- }
-
- public static int FindItem(byte byPackage, int templateId, int baseIdx = 0)
- {
- var slots = EnsureSlots(byPackage);
- if (baseIdx < 0) baseIdx = 0;
- for (int i = baseIdx; i < slots.Length; i++)
- {
- var it = slots[i];
- if (it != null && it.m_tid == templateId)
- return i;
- }
- return -1;
- }
-
- public static int GetItemTotalNum(byte byPackage, int templateId)
- {
- int total = 0;
- var slots = EnsureSlots(byPackage);
- for (int i = 0; i < slots.Length; i++)
- {
- var it = slots[i];
- if (it != null && it.m_tid == templateId)
- total += Math.Max(0, it.m_iCount);
- }
- return total;
- }
-
- public static int GetItemCanPileCount(byte byPackage, int templateId)
- {
- int ret = 0;
- var slots = EnsureSlots(byPackage);
- for (int i = 0; i < slots.Length; i++)
- {
- var it = slots[i];
- if (it != null && it.m_tid == templateId)
- {
- int pileLimit = Math.Max(1, EC_IvtrItem.GetPileLimit(templateId));
- ret += Math.Max(0, pileLimit - Math.Max(0, it.m_iCount));
- }
- }
- return ret;
- }
-
- public static int CanAddItem(byte byPackage, int templateId, int amount, bool tryPile)
- {
- int firstEmpty = -1;
- int pileLimit = Math.Max(1, EC_IvtrItem.GetPileLimit(templateId));
- var slots = EnsureSlots(byPackage);
- for (int i = 0; i < slots.Length; i++)
- {
- var it = slots[i];
- if (it == null)
- {
- // return first empty slot if not trying to pile item
- if (!tryPile) return i;
- if (firstEmpty < 0) firstEmpty = i;
- }
- else if (it.m_tid == templateId && it.m_iCount + amount <= pileLimit)
- {
- return i;
- }
- }
- return firstEmpty;
- }
-
- public static bool MergeItem(byte byPackage, int templateId, int expireDate, int amount, out int lastSlot, out int slotAmount)
- {
- lastSlot = -1;
- slotAmount = 0;
- var slots = EnsureSlots(byPackage);
- int pileLimit = Math.Max(1, EC_IvtrItem.GetPileLimit(templateId));
- int firstEmpty = -1;
-
- for (int i = 0; i < slots.Length && amount > 0; i++)
- {
- var slotItem = slots[i];
- if (slotItem == null)
- {
- if (firstEmpty < 0) firstEmpty = i;
- continue;
- }
- if (slotItem.m_tid != templateId) continue;
- int canAdd = Math.Max(0, pileLimit - Math.Max(0, slotItem.m_iCount));
- if (canAdd <= 0) continue;
- int add = Math.Min(canAdd, amount);
- slotItem.m_iCount += add;
- amount -= add;
- lastSlot = i;
- slotAmount = slotItem.m_iCount;
- }
- if (amount <= 0) return true;
- if (firstEmpty < 0) return false;
-
- var newItem = new EC_IvtrItem
- {
- Package = byPackage,
- Slot = firstEmpty,
- m_tid = templateId,
- m_expire_date = expireDate,
- State = 0,
- m_iCount = amount,
- Crc = 0,
- Content = null
- };
- slots[firstEmpty] = newItem;
- lastSlot = firstEmpty;
- slotAmount = amount;
- return true;
- }
-
- public static bool MoveItem(byte byPackage, int src, int dest, int amount)
- {
- if (src == dest) return false;
- if (amount <= 0) return false;
-
- var slots = EnsureSlots(byPackage);
- if (src < 0 || src >= slots.Length || dest < 0 || dest >= slots.Length) return false;
-
- var srcItem = slots[src];
- var dstItem = slots[dest];
- if (srcItem == null) return false;
-
- if (dstItem == null)
- {
- var clone = new EC_IvtrItem
- {
- Package = byPackage,
- Slot = dest,
- m_tid = srcItem.m_tid,
- m_expire_date = srcItem.m_expire_date,
- State = srcItem.State,
- m_iCount = amount,
- Crc = srcItem.Crc,
- Content = srcItem.Content != null ? (byte[])srcItem.Content.Clone() : null
- };
- slots[dest] = clone;
- }
- else
- {
- if (dstItem.m_tid != srcItem.m_tid) return false;
- int pileLimit = Math.Max(1, EC_IvtrItem.GetPileLimit(dstItem.m_tid));
- int canAdd = Math.Max(0, pileLimit - Math.Max(0, dstItem.m_iCount));
- int add = Math.Min(canAdd, amount);
- if (add <= 0) return false;
- dstItem.m_iCount += add;
- amount = add;
- }
- RemoveItem(byPackage, src, amount);
- return true;
- }
-
- public static void UpdatePack(byte byPackage, int ivtrSize, IEnumerable items)
- {
- Resize(byPackage, ivtrSize);
- var slots = EnsureSlots(byPackage);
-
- // Clear existing entries; keep size.
- for (int i = 0; i < slots.Length; i++)
- slots[i] = null;
-
- if (items != null)
- {
- foreach (var it in items)
- {
- if (it != null && it.Slot >= 0 && it.Slot < slots.Length)
- {
- slots[it.Slot] = it;
- }
- }
- }
- // Log this pack's items
- LogPackInternal(byPackage, ivtrSize, slots);
- }
-
- public static bool ResetWithDetailData(byte byPackage, int ivtrSize, byte[] data)
- {
- // Uses EC_IvtrItem.TryParseInventoryDetail format
- if (data == null)
- {
- Resize(byPackage, ivtrSize);
- RemoveAllItems(byPackage);
- return true;
- }
- if (!EC_IvtrItemUtils.Instance.TryParseInventoryDetail(data, out var pkg, out var size, out var items))
- return false;
- // Prefer header values when valid
- byte finalPkg = byPackage;
- if (pkg == byPackage) finalPkg = pkg;
- int finalSize = ivtrSize > 0 ? ivtrSize : size;
- UpdatePack(finalPkg, finalSize, items);
- return true;
- }
-
- private static void LogPackInternal(byte byPackage, int ivtrSize, EC_IvtrItem[] slots)
- {
- //Debug.Log($"[Inventory] === Pack {GetPackageName(byPackage)}({byPackage}) size={ivtrSize}, items={slots?.Length ?? 0} ===");
- if (slots == null || slots.Length == 0)
- {
- //Debug.Log("[Inventory] (empty)");
- return;
- }
- for (int i = 0; i < slots.Length; i++)
- {
- var it = slots[i];
- if (it == null)
- continue;
-
- string itemName = EC_IvtrItemUtils.Instance.ResolveItemName(it.m_tid);
- string extraHex = it.Content != null && it.Content.Length > 0 ? EC_IvtrItemUtils.Instance.BytesToHex(it.Content, MaxContentHexToLog) : "";
- //int extraLen = it.Content?.Length ?? 0;
- //Debug.Log(
- // $"[Inventory] pkg={GetPackageName(it.Package)}({it.Package}) slot={it.Slot} tid={it.TemplateId}{(string.IsNullOrEmpty(itemName) ? "" : " \"" + itemName + "\"")} count={it.Count} state={it.State} expire={it.ExpireDate} crc={it.Crc} content_len={extraLen}{(extraLen > 0 ? ", content_hex=" + extraHex : "")}"
- //);
- }
- }
-
- public static void LogInventoryPacket(string tag, byte[] buffer, int hostId)
- {
- if (buffer == null)
- {
- return;
- }
-
- int index = 0;
- if (buffer.Length < 6)
- {
- //LogInventoryRaw(tag, buffer);
- return;
- }
-
- byte byPackage = buffer[index++];
- byte ivtrSize = buffer[index++];
- uint contentLength = BitConverter.ToUInt32(buffer, index); index += 4;
-
- int remaining = buffer.Length - index;
- int contentBytes = remaining;
- if (contentLength < (uint)remaining)
- {
- contentBytes = (int)contentLength;
- }
-
- if (contentBytes > 0)
- {
- byte[] content = new byte[contentBytes];
- Buffer.BlockCopy(buffer, index, content, 0, contentBytes);
- }
-
- int trailing = buffer.Length - (index + contentBytes);
- if (trailing > 0)
- {
- byte[] tail = new byte[trailing];
- Buffer.BlockCopy(buffer, index + contentBytes, tail, 0, trailing);
- }
- }
-
- public static void LogInventoryRaw(string tag, byte[] buffer)
- {
- Debug.Log($"[Inventory] {tag}: RAW HEX (len={buffer?.Length ?? 0})=\n{(buffer == null ? "" : BitConverter.ToString(buffer))}");
- }
- }
-}
-
diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_Inventory.cs b/Assets/PerfectWorld/Scripts/Managers/EC_Inventory.cs
new file mode 100644
index 0000000000..2109586757
--- /dev/null
+++ b/Assets/PerfectWorld/Scripts/Managers/EC_Inventory.cs
@@ -0,0 +1,305 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace BrewMonster.Scripts.Managers
+{
+ ///
+ /// 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) =====
+
+ // Item array: index is slot, null means empty.
+ private EC_IvtrItem[] m_aItems = Array.Empty();
+
+ 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 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;
+ return old;
+ }
+
+ public void SetItem(int iSlot, EC_IvtrItem pItem)
+ {
+ if (iSlot < 0 || iSlot >= m_aItems.Length)
+ {
+ return;
+ }
+
+ m_aItems[iSlot] = pItem;
+ }
+
+ 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;
+ }
+
+ 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)
+ {
+ if (slotItem.GetTemplateID() != tid)
+ continue;
+
+ int pileLimit = Math.Max(1, EC_IvtrItem.GetPileLimit(tid));
+ int canAdd = Math.Max(0, pileLimit - Math.Max(0, slotItem.GetCount()));
+ if (canAdd <= 0) continue;
+
+ int add = Math.Min(canAdd, iAmount);
+ slotItem.AddAmount(add);
+ iAmount -= add;
+
+ if (iAmount == 0)
+ {
+ piLastSlot = i;
+ piLastAmount = slotItem.GetCount();
+ return true;
+ }
+ }
+ else if (firstEmpty < 0)
+ {
+ firstEmpty = i;
+ }
+ }
+
+ if (firstEmpty < 0 || iAmount <= 0)
+ {
+ return false;
+ }
+
+ var newItem = new EC_IvtrItem(tid, iExpireDate)
+ {
+ Slot = firstEmpty,
+ State = 0,
+ Crc = 0,
+ Content = null
+ };
+ newItem.SetCount(iAmount);
+
+ m_aItems[firstEmpty] = newItem;
+ piLastSlot = firstEmpty;
+ piLastAmount = iAmount;
+ 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 = new EC_IvtrItem(pSrc.GetTemplateID(), pSrc.GetExpireDate())
+ {
+ Slot = iDest,
+ Package = pSrc.Package,
+ State = pSrc.State,
+ Crc = pSrc.Crc,
+ Content = pSrc.Content != null ? (byte[])pSrc.Content.Clone() : null
+ };
+ clone.SetCount(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);
+ 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;
+
+ 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;
+ }
+ }
+}
diff --git a/Assets/PerfectWorld/Scripts/Managers/CECInventory.cs.meta b/Assets/PerfectWorld/Scripts/Managers/EC_Inventory.cs.meta
similarity index 100%
rename from Assets/PerfectWorld/Scripts/Managers/CECInventory.cs.meta
rename to Assets/PerfectWorld/Scripts/Managers/EC_Inventory.cs.meta
diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs b/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs
index 29f0904155..181114ab42 100644
--- a/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs
+++ b/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs
@@ -10,6 +10,7 @@ using BrewMonster;
using BrewMonster.Common;
using ModelRenderer.Scripts.GameData;
using PerfectWorld.Scripts.Managers;
+using BrewMonster.Scripts;
namespace BrewMonster.Scripts.Managers
{
@@ -654,34 +655,23 @@ namespace BrewMonster.Scripts.Managers
// === MVC: Model ===
private class InventoryModel
{
- private readonly FieldInfo itemsByPackageField;
-
- public InventoryModel()
- {
- var inventoryType = typeof(EC_Inventory);
- itemsByPackageField = inventoryType.GetField("_itemsByPackage", BindingFlags.NonPublic | BindingFlags.Static);
- if (itemsByPackageField == null)
- {
- Debug.LogError("[InventoryUI] Could not access _itemsByPackage field from EC_Inventory");
- }
- }
-
public Dictionary GetInventoryData(byte package)
{
- if (itemsByPackageField == null)
+ // Read from host player's per-package CECInventory instance
+ var host = CECGameRun.Instance?.GetHostPlayer();
+ var inv = host?.GetInventory(package);
+ var result = new Dictionary();
+ if (inv == null)
+ return result;
+
+ int size = inv.GetSize();
+ for (int i = 0; i < size; i++)
{
- return new Dictionary();
+ var item = inv.GetItem(i, false);
+ if (item != null)
+ result[i] = item;
}
- var itemsByPackage = itemsByPackageField.GetValue(null) as Dictionary>;
- if (itemsByPackage == null)
- {
- return new Dictionary();
- }
- if (itemsByPackage.TryGetValue(package, out var packageItems))
- {
- return packageItems;
- }
- return new Dictionary();
+ return result;
}
}
diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem.cs b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem.cs
index bac2feae4a..dcf525698d 100644
--- a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem.cs
+++ b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem.cs
@@ -708,7 +708,7 @@ namespace BrewMonster.Scripts.Managers
public bool m_bIsInNPCPack; // true, this item is in NPC package
public bool m_bLocalDetailData; // true, data from GetDetailDataFromLocal
- public CECInventory m_pDescIvtr; // Inventory only used to get item description
+ public EC_Inventory m_pDescIvtr; // Inventory only used to get item description
#region Constructors
@@ -979,7 +979,7 @@ namespace BrewMonster.Scripts.Managers
/// Get item description text (normal / booth-buy / reward / repair).
/// Mirrors the inline C++ GetDesc.
///
- public string GetDesc(DescType iDescType = DescType.DESC_NORMAL, CECInventory pInventory = null)
+ public string GetDesc(DescType iDescType = DescType.DESC_NORMAL, EC_Inventory pInventory = null)
{
m_pDescIvtr = pInventory;
diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrType.cs b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrType.cs
index bde0c2132f..b42a4e4aba 100644
--- a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrType.cs
+++ b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrType.cs
@@ -442,15 +442,18 @@ namespace BrewMonster.Scripts.Managers
///
private static IndexOfIteminEquipmentInventory GetAvailableFingerSlot()
{
+ var host = CECGameRun.Instance?.GetHostPlayer();
+ var equipInv = host?.GetInventory(InventoryConst.IVTRTYPE_EQUIPPACK);
+
// Check if FINGER1 slot is empty
- var finger1Item = EC_Inventory.GetItem(EC_Inventory.IVTRTYPE_EQUIPPACK, (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER1, false);
+ var finger1Item = equipInv?.GetItem((int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER1, false);
if (finger1Item == null)
{
return IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER1;
}
// Check if FINGER2 slot is empty
- var finger2Item = EC_Inventory.GetItem(EC_Inventory.IVTRTYPE_EQUIPPACK, (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER2, false);
+ var finger2Item = equipInv?.GetItem((int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER2, false);
if (finger2Item == null)
{
return IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER2;
diff --git a/Assets/PerfectWorld/Scripts/Task/CECTaskInterface.cs b/Assets/PerfectWorld/Scripts/Task/CECTaskInterface.cs
index f3c3990cd2..3d3b764b3e 100644
--- a/Assets/PerfectWorld/Scripts/Task/CECTaskInterface.cs
+++ b/Assets/PerfectWorld/Scripts/Task/CECTaskInterface.cs
@@ -454,12 +454,10 @@ namespace BrewMonster.Scripts.Task
public int GetCommonItemCount(uint ulCommonItem)
{
// CECInventory pPack = m_pHost.GetPack();
- //EC_Inventory pPack = m_pHost.GetPack();
-
- // return pPack != null ? EC_Inventory.GetItemTotalNum( 0, (int)ulCommonItem) : 0;
+ // return pPack != null ? pPack.GetItemTotalNum((int)ulCommonItem) : 0;
- // paramater 1 to get type inventory (0:inventoty, 1:equip,...)
- return EC_Inventory.GetItemTotalNum(EC_Inventory.IVTRTYPE_PACK, (int)ulCommonItem);
+ var inv = m_pHost?.GetInventory(InventoryConst.IVTRTYPE_PACK);
+ return inv != null ? inv.GetItemTotalNum((int)ulCommonItem) : 0;
}
public int GetTaskItemCount(uint ulTaskItem)
@@ -467,7 +465,8 @@ namespace BrewMonster.Scripts.Task
// CECInventory* pPack = m_pHost->GetTaskPack();
// return pPack ? pPack->GetItemTotalNum((int)ulTaskItem) : 0;
- return EC_Inventory.GetItemTotalNum(EC_Inventory.IVTRTYPE_TASKPACK, (int)ulTaskItem);
+ var inv = m_pHost?.GetInventory(InventoryConst.IVTRTYPE_TASKPACK);
+ return inv != null ? inv.GetItemTotalNum((int)ulTaskItem) : 0;
}
private ATaskTemplMan GetTaskTemplMan()
@@ -725,7 +724,8 @@ namespace BrewMonster.Scripts.Task
public uint GetInvEmptySlot()
{
- return (uint)EC_Inventory.GetEmptySlotNum(EC_Inventory.IVTRTYPE_PACK);
+ var inv = m_pHost?.GetInventory(InventoryConst.IVTRTYPE_PACK);
+ return inv != null ? (uint)inv.GetEmptySlotNum() : 0u;
}
public int GetFactionContrib()
diff --git a/Assets/Prefabs/UI/InventoryUI.prefab b/Assets/Prefabs/UI/InventoryUI.prefab
index 8fead78cbd..4693a4da1b 100644
--- a/Assets/Prefabs/UI/InventoryUI.prefab
+++ b/Assets/Prefabs/UI/InventoryUI.prefab
@@ -718,7 +718,6 @@ GameObject:
m_Component:
- component: {fileID: 5275130098495308601}
- component: {fileID: 7874153745862569389}
- - component: {fileID: 112648634020396885}
- component: {fileID: 3332719603249310962}
m_Layer: 5
m_Name: PreviewCamera
@@ -797,14 +796,6 @@ Camera:
m_OcclusionCulling: 1
m_StereoConvergence: 10
m_StereoSeparation: 0.022
---- !u!81 &112648634020396885
-AudioListener:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 633515748786992396}
- m_Enabled: 1
--- !u!114 &3332719603249310962
MonoBehaviour:
m_ObjectHideFlags: 0
diff --git a/Assets/Scripts/CECHostPlayer.cs b/Assets/Scripts/CECHostPlayer.cs
index f9cb9061a6..e532a65bd2 100644
--- a/Assets/Scripts/CECHostPlayer.cs
+++ b/Assets/Scripts/CECHostPlayer.cs
@@ -116,6 +116,31 @@ public partial class CECHostPlayer : CECPlayer
public bool IsChangingFace() { return m_bChangingFace; }
+ // ===== Inventory packs (instance-based) =====
+ // 0 = normal pack, 1 = equip pack, 2 = task pack (see InventoryConst.IVTRTYPE_*)
+ private readonly EC_Inventory m_packInventory = new EC_Inventory();
+ private readonly EC_Inventory m_equipInventory = new EC_Inventory();
+ private readonly EC_Inventory m_taskInventory = new EC_Inventory();
+
+ public EC_Inventory PackInventory => m_packInventory;
+ public EC_Inventory EquipInventory => m_equipInventory;
+ public EC_Inventory TaskInventory => m_taskInventory;
+
+ public EC_Inventory GetInventory(byte byPackage)
+ {
+ switch (byPackage)
+ {
+ case InventoryConst.IVTRTYPE_PACK:
+ return m_packInventory;
+ case InventoryConst.IVTRTYPE_EQUIPPACK:
+ return m_equipInventory;
+ case InventoryConst.IVTRTYPE_TASKPACK:
+ return m_taskInventory;
+ default:
+ return null;
+ }
+ }
+
private void Awake()
{
base.Awake();
@@ -773,8 +798,9 @@ public partial class CECHostPlayer : CECPlayer
Content = null
};
- // Add item to inventory
- EC_Inventory.SetItem(where, index, newItem);
+ // Add item to inventory (instance-based)
+ var inv = GetInventory(where);
+ inv?.SetItem(index, newItem);
Debug.Log(
$"[HOST_OBTAIN_ITEM] Successfully added item {type} to package {where}, slot {index} with count {amount}");
@@ -824,8 +850,9 @@ public partial class CECHostPlayer : CECPlayer
Content = null
};
- // Add item to inventory
- EC_Inventory.SetItem(byPackage, bySlot, newItem);
+ // Add item to inventory (instance-based)
+ var inv = GetInventory(byPackage);
+ inv?.SetItem(bySlot, newItem);
//Debug.Log($"[Inventory] Successfully added item {tid} to package {byPackage}, slot {bySlot} with count {iAmount}");
@@ -865,7 +892,8 @@ public partial class CECHostPlayer : CECPlayer
$"[Inventory] PLAYER_DROP_ITEM: package={byPackage}, slot={bySlot}, count={count}, tid={tid}, reason={reason}");
// Update the inventory by removing the item
- bool success = EC_Inventory.RemoveItem(byPackage, bySlot, count);
+ var inv = GetInventory(byPackage);
+ bool success = inv != null && inv.RemoveItem(bySlot, count);
if (success)
{
@@ -896,20 +924,22 @@ public partial class CECHostPlayer : CECPlayer
byte index_inv = data[0];
byte index_equip = data[1];
// Update client-side data: move item between PACK_INVENTORY and PACK_EQUIPMENT
- var invItem = EC_Inventory.GetItem(EC_Inventory.IVTRTYPE_PACK, index_inv, true);
- var equipItem = EC_Inventory.GetItem(EC_Inventory.IVTRTYPE_EQUIPPACK, index_equip, true);
+ var packInv = GetInventory(InventoryConst.IVTRTYPE_PACK);
+ var equipInv = GetInventory(InventoryConst.IVTRTYPE_EQUIPPACK);
+ var invItem = packInv?.GetItem(index_inv, true);
+ var equipItem = equipInv?.GetItem(index_equip, true);
if (invItem != null)
{
- invItem.Package = EC_Inventory.IVTRTYPE_EQUIPPACK;
+ invItem.Package = InventoryConst.IVTRTYPE_EQUIPPACK;
invItem.Slot = index_equip;
- EC_Inventory.SetItem(EC_Inventory.IVTRTYPE_EQUIPPACK, index_equip, invItem);
+ equipInv?.SetItem(index_equip, invItem);
}
if (equipItem != null)
{
- equipItem.Package = EC_Inventory.IVTRTYPE_PACK;
+ equipItem.Package = InventoryConst.IVTRTYPE_PACK;
equipItem.Slot = index_inv;
- EC_Inventory.SetItem(EC_Inventory.IVTRTYPE_PACK, index_inv, equipItem);
+ packInv?.SetItem(index_inv, equipItem);
}
// Trigger UI refresh if an EC_InventoryUI is present in scene
@@ -934,7 +964,7 @@ public partial class CECHostPlayer : CECPlayer
Debug.Log("[Inventory] OWN_ITEM_INFO received");
var data = Msg.dwParam1 as byte[];
int hostId = Convert.ToInt32(Msg.dwParam3);
- EC_Inventory.LogInventoryPacket("OWN_ITEM_INFO", data, hostId);
+ LogInventoryPacket("OWN_ITEM_INFO", data, hostId);
break;
}
}
@@ -950,7 +980,7 @@ public partial class CECHostPlayer : CECPlayer
{
case CommandID.OWN_IVTR_DATA:
{
- EC_Inventory.LogInventoryPacket("OWN_IVTR_DATA", data, hostId);
+ LogInventoryPacket("OWN_IVTR_DATA", data, hostId);
break;
}
case CommandID.OWN_IVTR_DETAIL_DATA:
@@ -964,11 +994,25 @@ public partial class CECHostPlayer : CECPlayer
if (EC_IvtrItemUtils.Instance.TryParseInventoryDetail(data, out var pkg,
out var size, out var items))
{
- EC_Inventory.UpdatePack(pkg, size, items);
+ var inv = GetInventory(pkg);
+ if (inv != null)
+ {
+ inv.Resize(size);
+ inv.RemoveAllItems();
+
+ if (items != null)
+ {
+ foreach (var it in items)
+ {
+ if (it != null && it.Slot >= 0 && it.Slot < size)
+ inv.SetItem(it.Slot, it);
+ }
+ }
+ }
}
// check if we got the item from the Equipment Pack. If so, we have to load the equipment items
- if (byPackage == EC_Inventory.IVTRTYPE_EQUIPPACK)
+ if (byPackage == InventoryConst.IVTRTYPE_EQUIPPACK)
{
UpdateEquipSkins();
}
@@ -1063,6 +1107,49 @@ public partial class CECHostPlayer : CECPlayer
}
}
+ // ===== Inventory packet logging helpers (was static on EC_Inventory/CECInventory) =====
+ private void LogInventoryPacket(string tag, byte[] buffer, int hostId)
+ {
+ if (buffer == null)
+ return;
+
+ int index = 0;
+ if (buffer.Length < 6)
+ {
+ //LogInventoryRaw(tag, buffer);
+ return;
+ }
+
+ byte byPackage = buffer[index++];
+ byte ivtrSize = buffer[index++];
+ uint contentLength = BitConverter.ToUInt32(buffer, index); index += 4;
+
+ int remaining = buffer.Length - index;
+ int contentBytes = remaining;
+ if (contentLength < (uint)remaining)
+ {
+ contentBytes = (int)contentLength;
+ }
+
+ if (contentBytes > 0)
+ {
+ byte[] content = new byte[contentBytes];
+ Buffer.BlockCopy(buffer, index, content, 0, contentBytes);
+ }
+
+ int trailing = buffer.Length - (index + contentBytes);
+ if (trailing > 0)
+ {
+ byte[] tail = new byte[trailing];
+ Buffer.BlockCopy(buffer, index + contentBytes, tail, 0, trailing);
+ }
+ }
+
+ private void LogInventoryRaw(string tag, byte[] buffer)
+ {
+ Debug.Log($"[Inventory] {tag}: RAW HEX (len={buffer?.Length ?? 0})=\n{(buffer == null ? "" : BitConverter.ToString(buffer))}");
+ }
+
public void OnMsgHstCorrectPos(in ECMSG Msg)
{
Debug.LogWarning("HoangDev : OnMsgHstCorrectPos");
diff --git a/Assets/Scripts/CECPlayer_Inventory.cs b/Assets/Scripts/CECPlayer_Inventory.cs
index fb27db140a..516a114e86 100644
--- a/Assets/Scripts/CECPlayer_Inventory.cs
+++ b/Assets/Scripts/CECPlayer_Inventory.cs
@@ -129,17 +129,16 @@ public partial class CECPlayer
public bool UpdateEquipSkins()
{
- var equipPack = EC_Inventory.GetPack(EC_Inventory.IVTRTYPE_EQUIPPACK);
-
int[] aNewEquips = new int[InventoryConst.IVTRSIZE_EQUIPPACK];
EC_IvtrItem pItem = null;
for (int i = 0; i < InventoryConst.IVTRSIZE_EQUIPPACK; i++)
{
-
- if (equipPack.TryGetValue(i, out pItem))
- {
+ // Use host player's equipment inventory (per-instance CECInventory)
+ var host = CECGameRun.Instance?.GetHostPlayer();
+ var equipInv = host?.EquipInventory;
+ pItem = equipInv?.GetItem(i, false);
+ if (pItem != null)
aNewEquips[i] = pItem.m_tid;
- }
}
ShowEquipments(aNewEquips, true, true);