diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_Inventory.cs b/Assets/PerfectWorld/Scripts/Managers/EC_Inventory.cs index 7d113a8309..cb5a5fde1c 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_Inventory.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_Inventory.cs @@ -159,6 +159,7 @@ namespace BrewMonster.Scripts.Managers 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; @@ -171,16 +172,8 @@ namespace BrewMonster.Scripts.Managers 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; + int iNumMerge = slotItem.MergeItem(tid, iAmount); + iAmount -= iNumMerge; if (iAmount == 0) { diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs b/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs index 8a3e9d53e7..cba791e16e 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs @@ -873,6 +873,23 @@ namespace BrewMonster.Scripts.Managers } } + /// + /// Stack count label under slot buttons: prefabs use text_quatity (typo) or text_quantity; legacy code used text_quality. + /// Only checks immediate children (Unity ); deeper layouts fall back to below. + /// + private static Transform FindStackCountTextTransform(Transform root) + { + if (root == null) return null; + string[] names = { "text_quality", "text_quatity", "text_quantity" }; + for (int n = 0; n < names.Length; n++) + { + var t = root.Find(names[n]); + if (t != null) return t; + } + + return null; + } + /// /// Update or create text component to show item count on the button /// @@ -885,22 +902,19 @@ namespace BrewMonster.Scripts.Managers TMPro.TextMeshProUGUI tmpText = null; Text legacyText = null; - // Find text component - var textTransform = button.transform.Find("text_quality"); + var textTransform = FindStackCountTextTransform(button.transform); if (textTransform != null) { tmpText = textTransform.GetComponent(); legacyText = textTransform.GetComponent(); } - else + + if (tmpText == null && legacyText == null) { - // Fallback: find any text in children tmpText = button.GetComponentInChildren(); if (tmpText == null) - { legacyText = button.GetComponentInChildren(); - } } // Update text diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrItem.cs b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrItem.cs index 8498617bb8..2314b84926 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrItem.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrItem.cs @@ -1211,14 +1211,12 @@ namespace BrewMonster.Scripts.Managers return iNumAdd; } - /// Add item amount. Returns new amount of item. + /// Add item amount. Returns new count (C++ a_Clamp to m_iPileLimit). public int AddAmount(int iAmount) { - Debug.Log($"[EC_IvtrItem] Old Amount: {m_iCount}"); m_iCount += iAmount; if (m_iCount < 0) m_iCount = 0; - //if (m_iCount > m_iPileLimit) m_iCount = m_iPileLimit; - Debug.Log($"[EC_IvtrItem] New Amount: {m_iCount}"); + if (m_iCount > m_iPileLimit) m_iCount = m_iPileLimit; return m_iCount; } diff --git a/Assets/Scripts/CECHostPlayer.ConfigData.cs b/Assets/Scripts/CECHostPlayer.ConfigData.cs index bd840e76df..27c715023d 100644 --- a/Assets/Scripts/CECHostPlayer.ConfigData.cs +++ b/Assets/Scripts/CECHostPlayer.ConfigData.cs @@ -482,6 +482,47 @@ namespace BrewMonster } } } + + /// C++ CECDealInventory::GetItemIndexByFlag + public int GetItemIndexByFlag(int iFlag) + { + int n = GetSize(); + for (int i = 0; i < n; i++) + { + if (GetItem(i, false) != null && m_aItemInfo[i].iFlag == iFlag) + return i; + } + return -1; + } + + /// C++ CECDealInventory::RemoveItemByFlag + public void RemoveItemByFlag(int iFlag, int iAmount) + { + int n = GetSize(); + for (int i = 0; i < n; i++) + { + var slotItem = GetItem(i, false); + if (slotItem == null) + continue; + + var info = m_aItemInfo[i]; + if (info.iFlag != iFlag) + continue; + + if (iAmount < 0 || iAmount >= info.iAmount) + { + SetItem(i, null); + } + else + { + info.iAmount -= iAmount; + m_aItemInfo[i] = info; + if (info.bDelete) + slotItem.AddAmount(-iAmount); + } + return; + } + } } // ��̯��Ʊ�Զ�ת�� diff --git a/Assets/Scripts/CECHostPlayer.Inventory.cs b/Assets/Scripts/CECHostPlayer.Inventory.cs index 2d383f5e3b..83c2c72450 100644 --- a/Assets/Scripts/CECHostPlayer.Inventory.cs +++ b/Assets/Scripts/CECHostPlayer.Inventory.cs @@ -594,7 +594,13 @@ namespace BrewMonster UpdateEquipSkins(); } - /// Buy from NPC/booth: server sends PURCHASE_ITEM (cmd_purchase_item). C++ OnMsgHstPurchaseItems. + /// + /// Buy from NPC/booth: S2C PURCHASE_ITEM (cmd_purchase_item). + /// Port of CECHostPlayer::OnMsgHstPurchaseItems (EC_HostMsg.cpp): prefer MergeItem like the C++ client. + /// If merge lands on a different slot than inv_index (common when stacking into an earlier pile), we still accept + /// the merged slot—the strict C++ iLastSlot == inv_index check would skip updates and break buy/stack UI here. + /// Fallback: PutItemInSlot(inv_index) then MergeItem when merge alone fails. + /// public void OnMsgHstPurchaseItems(ECMSG Msg) { var data = Msg.dwParam1 as byte[]; @@ -609,6 +615,7 @@ namespace BrewMonster return; var slotsNeedingDetail = new System.Collections.Generic.List(); + bool purchaseSlotMismatch = false; for (int i = 0; i < header.item_count && index + itemSize <= data.Length; i++) { @@ -616,37 +623,58 @@ namespace BrewMonster int expire_date = BitConverter.ToInt32(data, index); index += 4; int count = (int)BitConverter.ToUInt32(data, index); index += 4; ushort inv_index = BitConverter.ToUInt16(data, index); index += 2; - index += 1; // booth_slot + byte booth_slot = data[index]; + index += 1; if (inv_index >= pPack.GetSize()) pPack.Resize(inv_index + 1); - bool placed = pPack.PutItemInSlot(inv_index, item_id, expire_date, count, out int lastSlot, out int slotNum); + int iLastSlot = 0; + int iSlotNum = 0; + bool placed = pPack.MergeItem(item_id, expire_date, count, out iLastSlot, out iSlotNum); + if (!placed) + placed = pPack.PutItemInSlot(inv_index, item_id, expire_date, count, out iLastSlot, out iSlotNum); + if (!placed) + placed = pPack.MergeItem(item_id, expire_date, count, out iLastSlot, out iSlotNum); if (!placed) { - placed = pPack.MergeItem(item_id, expire_date, count, out lastSlot, out slotNum); - if (!placed || lastSlot != inv_index) - continue; +#if UNITY_EDITOR + UnityEngine.Debug.LogWarning( + $"[OnMsgHstPurchaseItems] Could not place purchase tid={item_id} count={count} inv_index={inv_index}"); +#endif + continue; } - var pItem = pPack.GetItem(inv_index, false); + if (iLastSlot != inv_index) + purchaseSlotMismatch = true; + +#if UNITY_EDITOR + if (iLastSlot != inv_index) + { + UnityEngine.Debug.Log( + $"[OnMsgHstPurchaseItems] Using merge slot {iLastSlot} (packet inv_index={inv_index}) tid={item_id}"); + } +#endif + + var pItem = pPack.GetItem(iLastSlot, false); if (pItem != null) { pItem.Package = (byte)Inventory_type.IVTRTYPE_PACK; - pItem.Slot = inv_index; - int cid = pItem.GetClassID(); - if (pItem.IsEquipment() || - cid == (int)EC_IvtrItem.InventoryClassId.ICID_TASKNMMATTER || - cid == (int)EC_IvtrItem.InventoryClassId.ICID_TASKDICE || - cid == (int)EC_IvtrItem.InventoryClassId.ICID_TASKITEM || - cid == (int)EC_IvtrItem.InventoryClassId.ICID_GOBLIN_EXPPILL || - cid == (int)EC_IvtrItem.InventoryClassId.ICID_WEDDINGBOOKCARD || - cid == (int)EC_IvtrItem.InventoryClassId.ICID_WEDDINGINVITECARD || - cid == (int)EC_IvtrItem.InventoryClassId.ICID_SKILLTOME || - cid == (int)EC_IvtrItem.InventoryClassId.ICID_GOBLIN || - cid == (int)EC_IvtrItem.InventoryClassId.ICID_PETEGG) + pItem.Slot = iLastSlot; + // Keep stack size in sync with MergeItem/PutItemInSlot totals (authoritative for this slot). + if (iSlotNum > 0) + pItem.SetCount(iSlotNum); + if (pItem.IsEquipment()) + slotsNeedingDetail.Add((byte)iLastSlot); + } + + if (header.flag != 0 && GetBoothState() == 2) + { + var boothBPack = GetBoothBuyPack(); + if (boothBPack != null) { - slotsNeedingDetail.Add((byte)inv_index); + // C++: CDlgInfo FIXMSG_BOOTHBUY — no matching in-game UI hook here; update booth pack only. + boothBPack.RemoveItemByFlag(booth_slot, count); } } } @@ -656,8 +684,18 @@ namespace BrewMonster foreach (byte slot in slotsNeedingDetail) UnityGameSession.c2s_CmdGetItemInfo((byte)Inventory_type.IVTRTYPE_PACK, slot); - var ui = GameObject.FindFirstObjectByType(); - ui?.RefreshAll(); + { + var ui = GameObject.FindFirstObjectByType(); + ui?.RefreshAll(); + if (purchaseSlotMismatch) + { + UnityGameSession.RequestInventoryAsync(0, () => + { + var ui2 = GameObject.FindFirstObjectByType(); + ui2?.RefreshAll(); + }); + } + } UpdateEquipSkins(); }