diff --git a/Assets/PerfectWorld/Scripts/Inventory/EC_IvtrType.cs b/Assets/PerfectWorld/Scripts/Inventory/EC_IvtrType.cs index 9064fcc8d0..a4bd58d806 100644 --- a/Assets/PerfectWorld/Scripts/Inventory/EC_IvtrType.cs +++ b/Assets/PerfectWorld/Scripts/Inventory/EC_IvtrType.cs @@ -1,95 +1,100 @@ -namespace BrewMonster.Scripts -{ - public class InventoryConst - { - // Index of item in equipment inventory - public const int EQUIPIVTR_WEAPON = 0; - public const int EQUIPIVTR_HEAD = 1; - public const int EQUIPIVTR_NECK = 2; - public const int EQUIPIVTR_SHOULDER = 3; - public const int EQUIPIVTR_BODY = 4; - public const int EQUIPIVTR_WAIST = 5; - public const int EQUIPIVTR_LEG = 6; - public const int EQUIPIVTR_FOOT = 7; - public const int EQUIPIVTR_WRIST = 8; - public const int EQUIPIVTR_FINGER1 = 9; - public const int EQUIPIVTR_FINGER2 = 10; - public const int EQUIPIVTR_PROJECTILE = 11; - public const int EQUIPIVTR_FLYSWORD = 12; - public const int EQUIPIVTR_FASHION_BODY = 13; - public const int EQUIPIVTR_FASHION_LEG = 14; - public const int EQUIPIVTR_FASHION_FOOT = 15; - public const int EQUIPIVTR_FASHION_WRIST = 16; - public const int EQUIPIVTR_RUNE = 17; - public const int EQUIPIVTR_BIBLE = 18; - public const int EQUIPIVTR_SPEAKER = 19; - public const int EQUIPIVTR_AUTOHP = 20; - public const int EQUIPIVTR_AUTOMP = 21; - public const int EQUIPIVTR_POCKET = 22; - public const int EQUIPIVTR_GOBLIN = 23; - public const int EQUIPIVTR_CERTIFICATE = 24; - public const int EQUIPIVTR_FASHION_HEAD = 25; - public const int EQUIPIVTR_FORCE_TOKEN = 26; - public const int EQUIPIVTR_DYNSKILLEQUIP1 = 27; - public const int EQUIPIVTR_DYNSKILLEQUIP2 = 28; - public const int EQUIPIVTR_FASHION_WEAPON = 29; - public const int SIZE_EQUIPIVTR = 30; - public const int EQUIPIVTR_UNUSED1 = 30; - public const int EQUIPIVTR_UNUSED2 = 31; - public const int EQUIPIVTR_GENERALCARD1 = 32; - public const int EQUIPIVTR_GENERALCARD2 = 33; - public const int EQUIPIVTR_GENERALCARD3 = 34; - public const int EQUIPIVTR_GENERALCARD4 = 35; - public const int EQUIPIVTR_GENERALCARD5 = 36; - public const int EQUIPIVTR_GENERALCARD6 = 37; - public const int SIZE_ALL_EQUIPIVTR = 38; - public const int SIZE_GENERALCARD_EQUIPIVTR = 6; // SIZE_ALL_EQUIPIVTR - EQUIPIVTR_GENERALCARD1 - - // Inventory size constants - public const int IVTRSIZE_EQUIPPACK = 32; // Equipment (SIZE_ALL_EQUIPIVTR) - public const int IVTRSIZE_TASKPACK = 32; // Task pack - public const int IVTRSIZE_DEALPACK = 24; // Deal pack - public const int IVTRSIZE_NPCPACK = 32; // NPC pack - public const int IVTRSIZE_TRASHBOX = 16; // Trash box - public const int IVTRSIZE_BUYPACK = 12; // Buy pack - public const int IVTRSIZE_SELLPACK = 12; // Sell pack - public const int IVTRSIZE_BOOTHSPACK = 12; // Default booth pack for selling - public const int IVTRSIZE_BOOTHBPACK = 12; // Default booth pack for buying - public const int IVTRSIZE_BOOTHSPACK_MAX = 20; // Max booth pack for selling (player may use certificate...) - public const int IVTRSIZE_BOOTHBPACK_MAX = 20; // Max booth pack for buying - - public const int IVTRSIZE_CLIENTCARDPACK = 32; // Client pack for general card collection - - // Main bag UI (C++ DlgInventory.h: CECDLGSHOP_PACKMAX / PACKLINEMAX) - public const int IVTRSIZE_PACK_UI_PAGE = 32; - public const int IVTRSIZE_PACK_UI_LINE = 8; - /// Upper bound for client pack slots (server ivtr_size is byte). - public const int IVTRSIZE_PACK_MAX = 255; - - 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; - } - public enum Shortcut - { - NUM_HOSTSCSETS1 = 5, // expanded from 3 to 5 (2009.05.27) - NUM_HOSTSCSETS2 = 3, - SIZE_HOSTSCSET1 = 9, // expanded from 6 to 9 (2009.05.27) - SIZE_HOSTSCSET2 = 8, - - SIZE_FASHIONSCSET = 240, - }; - // Pet food type - enum PetFoodType - { - PET_FOOD_GRASS = 0, - PET_FOOD_MEAT, - PET_FOOD_VEGETABLE, - PET_FOOD_FRUIT, - PET_FOOD_WATER, - MAX_PET_FOOD, - }; +namespace BrewMonster.Scripts +{ + public class InventoryConst + { + // Index of item in equipment inventory + public const int EQUIPIVTR_WEAPON = 0; + public const int EQUIPIVTR_HEAD = 1; + public const int EQUIPIVTR_NECK = 2; + public const int EQUIPIVTR_SHOULDER = 3; + public const int EQUIPIVTR_BODY = 4; + public const int EQUIPIVTR_WAIST = 5; + public const int EQUIPIVTR_LEG = 6; + public const int EQUIPIVTR_FOOT = 7; + public const int EQUIPIVTR_WRIST = 8; + public const int EQUIPIVTR_FINGER1 = 9; + public const int EQUIPIVTR_FINGER2 = 10; + public const int EQUIPIVTR_PROJECTILE = 11; + public const int EQUIPIVTR_FLYSWORD = 12; + public const int EQUIPIVTR_FASHION_BODY = 13; + public const int EQUIPIVTR_FASHION_LEG = 14; + public const int EQUIPIVTR_FASHION_FOOT = 15; + public const int EQUIPIVTR_FASHION_WRIST = 16; + public const int EQUIPIVTR_RUNE = 17; + public const int EQUIPIVTR_BIBLE = 18; + public const int EQUIPIVTR_SPEAKER = 19; + public const int EQUIPIVTR_AUTOHP = 20; + public const int EQUIPIVTR_AUTOMP = 21; + public const int EQUIPIVTR_POCKET = 22; + public const int EQUIPIVTR_GOBLIN = 23; + public const int EQUIPIVTR_CERTIFICATE = 24; + public const int EQUIPIVTR_FASHION_HEAD = 25; + public const int EQUIPIVTR_FORCE_TOKEN = 26; + public const int EQUIPIVTR_DYNSKILLEQUIP1 = 27; + public const int EQUIPIVTR_DYNSKILLEQUIP2 = 28; + public const int EQUIPIVTR_FASHION_WEAPON = 29; + public const int SIZE_EQUIPIVTR = 30; + public const int EQUIPIVTR_UNUSED1 = 30; + public const int EQUIPIVTR_UNUSED2 = 31; + public const int EQUIPIVTR_GENERALCARD1 = 32; + public const int EQUIPIVTR_GENERALCARD2 = 33; + public const int EQUIPIVTR_GENERALCARD3 = 34; + public const int EQUIPIVTR_GENERALCARD4 = 35; + public const int EQUIPIVTR_GENERALCARD5 = 36; + public const int EQUIPIVTR_GENERALCARD6 = 37; + public const int SIZE_ALL_EQUIPIVTR = 38; + public const int SIZE_GENERALCARD_EQUIPIVTR = 6; // SIZE_ALL_EQUIPIVTR - EQUIPIVTR_GENERALCARD1 + + // Inventory size constants + public const int IVTRSIZE_EQUIPPACK = 32; // Equipment (SIZE_ALL_EQUIPIVTR) + public const int IVTRSIZE_TASKPACK = 32; // Task pack + public const int IVTRSIZE_DEALPACK = 24; // Deal pack + public const int IVTRSIZE_NPCPACK = 32; // NPC pack + public const int IVTRSIZE_TRASHBOX = 16; // Trash box + public const int IVTRSIZE_BUYPACK = 12; // Buy pack + public const int IVTRSIZE_SELLPACK = 12; // Sell pack + public const int IVTRSIZE_BOOTHSPACK = 12; // Default booth pack for selling + public const int IVTRSIZE_BOOTHBPACK = 12; // Default booth pack for buying + public const int IVTRSIZE_BOOTHSPACK_MAX = 20; // Max booth pack for selling (player may use certificate...) + public const int IVTRSIZE_BOOTHBPACK_MAX = 20; // Max booth pack for buying + + public const int IVTRSIZE_CLIENTCARDPACK = 32; // Client pack for general card collection + + // Main bag UI (C++ DlgInventory.h: CECDLGSHOP_PACKMAX / PACKLINEMAX) + public const int IVTRSIZE_PACK_UI_PAGE = 32; + public const int IVTRSIZE_PACK_UI_LINE = 8; + /// Upper bound for client pack slots (server ivtr_size is byte). + public const int IVTRSIZE_PACK_MAX = 255; + + 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; + public const byte IVTRTYPE_TRASHBOX = 3; + public const byte IVTRTYPE_TRASHBOX2 = 4; + public const byte IVTRTYPE_TRASHBOX3 = 5; + public const byte IVTRTYPE_ACCOUNT_BOX = 6; + public const byte IVTRTYPE_GENERALCARD_BOX = 7; + } + public enum Shortcut + { + NUM_HOSTSCSETS1 = 5, // expanded from 3 to 5 (2009.05.27) + NUM_HOSTSCSETS2 = 3, + SIZE_HOSTSCSET1 = 9, // expanded from 6 to 9 (2009.05.27) + SIZE_HOSTSCSET2 = 8, + + SIZE_FASHIONSCSET = 240, + }; + // Pet food type + enum PetFoodType + { + PET_FOOD_GRASS = 0, + PET_FOOD_MEAT, + PET_FOOD_VEGETABLE, + PET_FOOD_FRUIT, + PET_FOOD_WATER, + MAX_PET_FOOD, + }; } \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs b/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs index e374f7d57e..a014dc3809 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs @@ -43,6 +43,8 @@ namespace BrewMonster.Scripts.Managers [SerializeField] private EC_UIUtility.TextOutlet descriptionText; [SerializeField] private Button equipButton; [SerializeField] private Button dropButton; + [Tooltip("Optional. When warehouse is open: Bỏ vào / Lấy ra. If unset, equipButton is reused.")] + [SerializeField] private Button storageTransferButton; [Header("Stack Split UI (assign in Inspector)")] [SerializeField] private GameObject splitPanelRoot; @@ -113,15 +115,19 @@ namespace BrewMonster.Scripts.Managers } // Current selected item for equip/unequip operations - private byte currentSelectedPackage; - private int currentSelectedSlot; + internal byte currentSelectedPackage; + internal int currentSelectedSlot; private EC_IvtrItem currentSelectedItem; private EC_IvtrItem currentSelectedEquipment; + private Transform _detailPanelOriginalParent; + private int _detailPanelOriginalSiblingIndex = -1; + private const byte PKG_INVENTORY = InventoryConst.IVTRTYPE_PACK; private const byte PKG_EQUIPMENT = InventoryConst.IVTRTYPE_EQUIPPACK; private const byte PKG_TASK = InventoryConst.IVTRTYPE_TASKPACK; private const byte PKG_FASHION = 3; // Trash / fashion box slot in legacy client (GetInventory may not resolve; see host) + private const byte PKG_TRASHBOX = InventoryConst.IVTRTYPE_TRASHBOX; public enum InventoryBagTab { @@ -132,6 +138,26 @@ namespace BrewMonster.Scripts.Managers private InventoryBagTab _bagTab = InventoryBagTab.Item; private bool _inventorySlotTemplateResolved; + public override void Show(bool value) + { + if (!value && GetHostPlayer() != null && GetHostPlayer().IsUsingTrashBox()) + { + CECHostPlayer.PopupStorageDialog(true); + return; + } + base.Show(value); + } + + public override void CloseDialogue() + { + if (GetHostPlayer() != null && GetHostPlayer().IsUsingTrashBox()) + { + CECHostPlayer.PopupStorageDialog(true); + return; + } + base.CloseDialogue(); + } + private void Awake() { model = new InventoryModel(); @@ -646,31 +672,42 @@ namespace BrewMonster.Scripts.Managers } } - private void OnInventoryButtonClicked(byte package, int slot) + /// Show item detail for pack or warehouse slot (click only opens detail). + public void ShowItemDetailForPackage(byte package, int slot) { - UnityGameSession.RequestCheckSecurityPassWd(""); - var data = model.GetInventoryData(package); - if (data != null && data.TryGetValue(slot, out var itemData)) + EC_IvtrItem itemData = null; + if (package == PKG_TRASHBOX) { - // Store current selection for equip/unequip operations - currentSelectedPackage = package; - currentSelectedSlot = slot; - currentSelectedItem = itemData; - - // Create equipment object if this is equipment - currentSelectedEquipment = CreateEquipmentFromItemData(itemData); - - // Position detail panel near the clicked item button - - FillDetailPanel(package, itemData); - PositionDetailPanelNearButton(package, slot); + var host = CECGameRun.Instance?.GetHostPlayer(); + itemData = host?.GetTrashBox()?.GetItem(slot, false); } else { - ShowDetailPanel(false); + var data = model.GetInventoryData(package); + data?.TryGetValue(slot, out itemData); + } + + if (itemData != null) + { + currentSelectedPackage = package; + currentSelectedSlot = slot; + currentSelectedItem = itemData; + currentSelectedEquipment = CreateEquipmentFromItemData(itemData); + FillDetailPanel(package, itemData); + PositionDetailPanelNearButton(package, slot); + EC_UIUtility.BringPanelToFront(detailPanelRoot.gameObject); + } + else + { + DismissItemDetail(); } } + public void DismissItemDetail() => ShowDetailPanel(false); + + private void OnInventoryButtonClicked(byte package, int slot) => + ShowItemDetailForPackage(package, slot); + /// /// Create EC_IvtrEquip object from InventoryItemData /// @@ -1465,14 +1502,50 @@ namespace BrewMonster.Scripts.Managers private void ShowDetailPanel(bool show) { - EC_UIUtility.ShowPanel(detailPanelRoot.gameObject, show); - if (!show) + if (detailPanelRoot == null) + return; + + if (show) { + AttachDetailPanelToDialogCanvas(); + EC_UIUtility.ShowPanel(detailPanelRoot.gameObject, true); + EC_UIUtility.BringPanelToFront(detailPanelRoot.gameObject); + } + else + { + EC_UIUtility.ShowPanel(detailPanelRoot.gameObject, false); + RestoreDetailPanelParent(); RefreshSplitControlsVisibility(0, null); RefreshCombineControlsVisibility(0, null); } } + void AttachDetailPanelToDialogCanvas() + { + var panelTransform = detailPanelRoot.transform; + var canvasRoot = CECUIManager.Instance?.DialogCanvasTransform; + if (canvasRoot == null || panelTransform.parent == canvasRoot) + return; + + _detailPanelOriginalParent = panelTransform.parent; + _detailPanelOriginalSiblingIndex = panelTransform.GetSiblingIndex(); + panelTransform.SetParent(canvasRoot, true); + } + + void RestoreDetailPanelParent() + { + if (_detailPanelOriginalParent == null) + return; + + var panelTransform = detailPanelRoot.transform; + panelTransform.SetParent(_detailPanelOriginalParent, true); + if (_detailPanelOriginalSiblingIndex >= 0) + panelTransform.SetSiblingIndex(_detailPanelOriginalSiblingIndex); + + _detailPanelOriginalParent = null; + _detailPanelOriginalSiblingIndex = -1; + } + /// /// Split affordance only for main bag stacks (same as ). /// If splitCloseButton is parented under splitPanelRoot, its visibility follows the modal (not this rule). @@ -1531,6 +1604,9 @@ namespace BrewMonster.Scripts.Managers private Button GetButtonForSlot(byte package, int slot) { + if (package == PKG_TRASHBOX) + return EC_StorageUI.GetSlotButtonStatic(slot); + List