diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs b/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs index 33ca375e57..6f18117f1f 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs @@ -49,6 +49,9 @@ namespace BrewMonster.Scripts.Managers [SerializeField] private Button splitDecreaseButton; [SerializeField] private Button splitMaxButton; + [Header("Stack combine — merge into another stack (C++ inventory drag-merge, assign in Inspector)")] + [SerializeField] private Button combineStackButton; + private int _splitAmount = 1; private int _splitMaxAmount = 1; @@ -126,6 +129,7 @@ namespace BrewMonster.Scripts.Managers view = new InventoryView(); WireBagTabButtons(); WireSplitUI(); + WireCombineUI(); //if (currentDragImage == null) //{ @@ -207,6 +211,15 @@ namespace BrewMonster.Scripts.Managers } } + private void WireCombineUI() + { + if (combineStackButton != null) + { + combineStackButton.onClick.RemoveAllListeners(); + combineStackButton.onClick.AddListener(() => CombineSelectedStack()); + } + } + private void ShowSplitPanel(bool show) { if (splitPanelRoot != null) @@ -777,6 +790,100 @@ namespace BrewMonster.Scripts.Managers return true; } + /// + /// Merge from the selected slot into another stack of the same template (C++ CDlgInventory::ExchangeItem + /// branch that calls c2s_CmdMoveIvtrItem when dest matches and pile limit allows). + /// Picks the lowest-index compatible destination with free stack space. + /// + public bool CombineSelectedStack() + { + if (currentSelectedItem == null) + { + Debug.LogWarning("[InventoryUI] CombineSelectedStack: no item selected"); + return false; + } + + if (currentSelectedPackage != PKG_INVENTORY) + { + Debug.LogWarning($"[InventoryUI] CombineSelectedStack: unsupported package {currentSelectedPackage} (only PKG_INVENTORY supported)"); + return false; + } + + if (!TryGetInventoryMergeTarget(currentSelectedSlot, currentSelectedItem, out int dstSlot, out int moveAmount)) + { + Debug.LogWarning("[InventoryUI] CombineSelectedStack: no merge target with free stack space"); + return false; + } + + UnityGameSession.RequestMoveIvtrItem((byte)currentSelectedSlot, (byte)dstSlot, (uint)moveAmount); + RefreshAll(); + return true; + } + + /// + /// Pile cap for stacking: instance (from essence pile_num_max) is authoritative; + /// may still be 1 if element reflection misses field names. + /// + private static int GetEffectivePileLimitForStack(int tid, EC_IvtrItem a, EC_IvtrItem b) + { + int p = Math.Max(1, EC_IvtrItem.GetPileLimit(tid)); + if (a != null) + p = Math.Max(p, Math.Max(1, a.GetPileLimitInstance())); + if (b != null) + p = Math.Max(p, Math.Max(1, b.GetPileLimitInstance())); + return p; + } + + /// First eligible slot (lowest index) that can accept part or all of . + private static bool TryGetInventoryMergeTarget(int srcSlot, EC_IvtrItem srcItem, out int dstSlot, out int moveAmount) + { + dstSlot = -1; + moveAmount = 0; + if (srcItem == null || srcItem.IsFrozen()) + return false; + + int tid = srcItem.GetTemplateID(); + int srcCount = srcItem.GetCount(); + if (srcCount < 1) + return false; + + var host = CECGameRun.Instance?.GetHostPlayer(); + var inv = host?.GetInventory(PKG_INVENTORY); + if (inv == null) + return false; + + int size = inv.GetSize(); + for (int i = 0; i < size; i++) + { + if (i == srcSlot) + continue; + + var dst = inv.GetItem(i, false); + if (dst == null || dst.IsFrozen()) + continue; + if (dst.GetTemplateID() != tid) + continue; + + int pile = GetEffectivePileLimitForStack(tid, srcItem, dst); + if (pile <= 1) + continue; + + int room = pile - dst.GetCount(); + if (room <= 0) + continue; + + int move = Math.Min(srcCount, room); + if (move <= 0) + continue; + + dstSlot = i; + moveAmount = move; + return true; + } + + return false; + } + private int FindEmptySlotInPackage(byte package) { var host = CECGameRun.Instance?.GetHostPlayer(); @@ -1200,7 +1307,10 @@ namespace BrewMonster.Scripts.Managers { EC_UIUtility.ShowPanel(detailPanelRoot.gameObject, show); if (!show) + { RefreshSplitControlsVisibility(0, null); + RefreshCombineControlsVisibility(0, null); + } } /// @@ -1245,6 +1355,20 @@ namespace BrewMonster.Scripts.Managers } } + private bool CanShowCombineControls(byte package, EC_IvtrItem item) + { + if (item == null || package != PKG_INVENTORY) + return false; + return TryGetInventoryMergeTarget(currentSelectedSlot, item, out _, out _); + } + + private void RefreshCombineControlsVisibility(byte package, EC_IvtrItem item) + { + bool canCombine = CanShowCombineControls(package, item); + if (combineStackButton != null) + combineStackButton.gameObject.SetActive(canCombine); + } + private Button GetButtonForSlot(byte package, int slot) { List