Merge branch 'develop' into feature/gfx-action

This commit is contained in:
vuong dinh hoang
2026-05-22 09:40:08 +07:00
29 changed files with 17307 additions and 179 deletions
@@ -63,3 +63,7 @@ MonoBehaviour:
prefab: {fileID: 6225996695219405878, guid: 2ed5e05eaa1d87341bf25c3cf111cc01, type: 3}
- id: Win_Settings
prefab: {fileID: 5193882765232824931, guid: 12e3fbc87fab9044abb62aba808775c8, type: 3}
- id: EC_StorageUI
prefab: {fileID: 3837460183159982207, guid: 0986049a141406946b0ed97344b84f78, type: 3}
- id: DlgQuantity
prefab: {fileID: 8147986291757959694, guid: 11d09ee52b0c5f24fb3ef21e177ebe2d, type: 3}
@@ -12,7 +12,10 @@ namespace BrewMonster.UI
public GameObject GetPrefabDialog(string id)
{
return lstPrefabDialog.Find(x => x.id.Equals(id)).prefab;
if (string.IsNullOrEmpty(id) || lstPrefabDialog == null)
return null;
var entry = lstPrefabDialog.Find(x => !string.IsNullOrEmpty(x.id) && x.id.Equals(id));
return entry.prefab;
}
}
@@ -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;
/// <summary>Upper bound for client pack slots (server ivtr_size is byte).</summary>
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;
/// <summary>Upper bound for client pack slots (server ivtr_size is byte).</summary>
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,
};
}
@@ -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)
/// <summary>Show item detail for pack or warehouse slot (click only opens detail).</summary>
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);
/// <summary>
/// Create EC_IvtrEquip object from InventoryItemData
/// </summary>
@@ -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;
}
/// <summary>
/// Split affordance only for main bag stacks (same as <see cref="SeparateSelectedStack"/>).
/// If <c>splitCloseButton</c> is parented under <c>splitPanelRoot</c>, 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<Button> list = null;
switch (package)
{
@@ -1621,9 +1697,9 @@ namespace BrewMonster.Scripts.Managers
// 完全按照C++代码获取扩展描述:g_pGame->GetItemExtDesc(m_tid)
// C++代码不检查IsInitialized() - 它直接调用GetWideString()
// Setup equip and drop buttons
SetupEquipButton(package, item);
SetupDropButton(package, item);
SetupStorageTransferButton(package, item);
// Show panel first
// 先显示面板
@@ -1640,7 +1716,14 @@ namespace BrewMonster.Scripts.Managers
{
if (equipButton == null) return;
// Clear previous listeners
var host = CECGameRun.Instance?.GetHostPlayer();
if (host != null && host.IsUsingTrashBox() &&
(package == PKG_INVENTORY || package == PKG_TRASHBOX))
{
equipButton.gameObject.SetActive(false);
return;
}
equipButton.onClick.RemoveAllListeners();
equipButton.onClick.AddListener(OnEquipButtonClicked);
@@ -1703,10 +1786,119 @@ namespace BrewMonster.Scripts.Managers
}
}
void SetupStorageTransferButton(byte package, EC_IvtrItem item)
{
var btn = storageTransferButton != null ? storageTransferButton : equipButton;
if (btn == null)
return;
var host = CECGameRun.Instance?.GetHostPlayer();
bool show = host != null && host.IsUsingTrashBox() &&
(package == PKG_INVENTORY || package == PKG_TRASHBOX);
btn.gameObject.SetActive(show);
if (!show)
return;
btn.onClick.RemoveAllListeners();
btn.onClick.AddListener(OnStorageTransferButtonClicked);
string label = package == PKG_INVENTORY ? "Bỏ vào" : "Lấy ra";
SetButtonLabel(btn, label);
}
static void SetButtonLabel(Button btn, string label)
{
var buttonText = btn.GetComponentInChildren<UnityEngine.UI.Text>();
if (buttonText != null)
buttonText.text = label;
else
{
var tmpText = btn.GetComponentInChildren<TMPro.TextMeshProUGUI>();
if (tmpText != null)
tmpText.text = label;
}
}
void OnStorageTransferButtonClicked()
{
var host = CECGameRun.Instance?.GetHostPlayer();
if (host == null || !host.IsUsingTrashBox() || currentSelectedItem == null)
return;
if (currentSelectedPackage == PKG_INVENTORY && currentSelectedSlot >= 0)
{
int trashSlot = FindTrashSlotForItem(host, currentSelectedItem);
if (trashSlot >= 0)
host.TransferPackAndTrash(PKG_TRASHBOX, trashSlot, currentSelectedSlot);
}
else if (currentSelectedPackage == PKG_TRASHBOX && currentSelectedSlot >= 0)
{
int invSlot = FindPackSlotForItem(host, currentSelectedItem);
if (invSlot >= 0)
host.TransferPackAndTrash(PKG_TRASHBOX, currentSelectedSlot, invSlot);
}
DismissItemDetail();
}
static int FindTrashSlotForItem(CECHostPlayer host, EC_IvtrItem item)
{
var trash = host?.GetTrashBox();
if (trash == null || item == null)
return -1;
int size = trash.GetSize();
int pile = item.GetPileLimitInstance();
if (pile > 1)
{
for (int i = 0; i < size; i++)
{
var dst = trash.GetItem(i);
if (dst != null && dst.m_tid == item.m_tid && dst.m_iCount < pile)
return i;
}
}
for (int i = 0; i < size; i++)
{
if (trash.GetItem(i) == null)
return i;
}
return -1;
}
static int FindPackSlotForItem(CECHostPlayer host, EC_IvtrItem item)
{
var pack = host?.GetPack();
if (pack == null || item == null)
return -1;
int size = pack.GetSize();
int pile = item.GetPileLimitInstance();
if (pile > 1)
{
for (int i = 0; i < size; i++)
{
var dst = pack.GetItem(i);
if (dst != null && dst.m_tid == item.m_tid && dst.m_iCount < pile)
return i;
}
}
for (int i = 0; i < size; i++)
{
if (pack.GetItem(i) == null)
return i;
}
return -1;
}
private void SetupDropButton(byte package, EC_IvtrItem item)
{
if (dropButton == null) return;
if (package == PKG_TRASHBOX)
{
dropButton.gameObject.SetActive(false);
return;
}
// Clear previous listeners
dropButton.onClick.RemoveAllListeners();
dropButton.onClick.AddListener(OnDropButtonClicked);
@@ -0,0 +1,90 @@
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace BrewMonster.Scripts.Managers
{
/// <summary>Shared stack count label lookup for inventory / storage slot buttons.</summary>
public static class EC_SlotStackCountDisplay
{
public static int ResolveStackCount(EC_IvtrItem item)
{
if (item == null)
return 0;
int n = item.GetCount();
if (n < 2 && item.m_iCount >= 2)
n = item.m_iCount;
return n;
}
public static void UpdateButton(Button button, EC_IvtrItem item)
{
UpdateButton(button, ResolveStackCount(item));
}
public static void UpdateButton(Button button, int count)
{
if (button == null)
return;
TMPro.TextMeshProUGUI tmpText = null;
Text legacyText = null;
var textTransform = FindStackCountTextTransform(button.transform);
if (textTransform != null)
{
tmpText = textTransform.GetComponent<TMPro.TextMeshProUGUI>();
legacyText = textTransform.GetComponent<Text>();
}
if (tmpText == null && legacyText == null)
{
tmpText = button.GetComponentInChildren<TMPro.TextMeshProUGUI>();
if (tmpText == null)
legacyText = button.GetComponentInChildren<Text>();
}
if (count > 1)
{
string countText = count.ToString();
if (tmpText != null)
{
tmpText.text = countText;
tmpText.gameObject.SetActive(true);
}
else if (legacyText != null)
{
legacyText.text = countText;
legacyText.gameObject.SetActive(true);
}
}
else
{
if (tmpText != null)
{
tmpText.text = "";
tmpText.gameObject.SetActive(false);
}
else if (legacyText != null)
{
legacyText.text = "";
legacyText.gameObject.SetActive(false);
}
}
}
static Transform FindStackCountTextTransform(Transform root)
{
if (root == null)
return null;
string[] names = { "Txt_Count", "text_quality", "text_quatity", "text_quantity", "image_amount" };
for (int n = 0; n < names.Length; n++)
{
var t = root.Find(names[n]);
if (t != null)
return t;
}
return null;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 25708cd8c6fa9b941babe1e2446f18ad
@@ -0,0 +1,279 @@
using BrewMonster;
using BrewMonster.Network;
using BrewMonster.Scripts;
using BrewMonster.Scripts.Managers;
using BrewMonster.UI;
using CSNetwork.GPDataType;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace BrewMonster.Scripts.Managers
{
/// <summary>
/// Warehouse UI (C++ CDlgStorage / Win_Storage). Works with <see cref="EC_InventoryUI"/> for pack↔storage transfer.
/// Assign storage slot buttons in Inspector (e.g. clone from inventory grid); dialog name: EC_StorageUI.
/// </summary>
public class EC_StorageUI : AUIDialog
{
[SerializeField] private List<Button> storageSlotButtons = new List<Button>();
[SerializeField] private Button closeButton;
[SerializeField] private List<Text> trashMoneyTextsLegacy = new List<Text>();
[SerializeField] private List<TMPro.TextMeshProUGUI> trashMoneyTextsTMP = new List<TMPro.TextMeshProUGUI>();
[Header("Storage actions (C++ CDlgStorage)")]
[Tooltip("Gửi tiền vào kho — C++ INPUTNO_STORAGE_IVTR_MONEY (túi → kho).")]
[SerializeField] private Button depositMoneyButton;
[Tooltip("Rút tiền ra — C++ INPUTNO_STORAGE_TRASH_MONEY (kho → túi).")]
[SerializeField] private Button withdrawMoneyButton;
[Tooltip("Sắp xếp kho (IVTRTYPE_TRASHBOX) — C++ CDlgStorage::OnCommand_arrange.")]
[SerializeField] private Button sortPackButton;
private const byte TrashWhere = InventoryConst.IVTRTYPE_TRASHBOX;
private int _selectedTrashSlot = -1;
private static EC_StorageUI s_instance;
private readonly Dictionary<Image, Sprite> _defaultSlotSprites = new Dictionary<Image, Sprite>();
public override void Awake()
{
base.Awake();
s_instance = this;
if (closeButton != null)
{
closeButton.onClick.RemoveAllListeners();
closeButton.onClick.AddListener(() => CECHostPlayer.PopupStorageDialog(true));
}
ResolveStorageActionButtons();
WireStorageActionButtons();
}
private void OnDestroy()
{
if (s_instance == this)
s_instance = null;
}
public static void RefreshAllStatic()
{
s_instance?.RefreshAll();
}
public static void RefreshMoneyStatic()
{
s_instance?.RefreshMoney();
}
public static void ClearSelectionStatic()
{
if (s_instance != null)
s_instance._selectedTrashSlot = -1;
}
public static bool TryGetSelectedTrashSlot(out int slot)
{
slot = s_instance?._selectedTrashSlot ?? -1;
return s_instance != null && slot >= 0;
}
public static Button GetSlotButtonStatic(int slot)
{
if (s_instance == null || slot < 0 || slot >= s_instance.storageSlotButtons.Count)
return null;
return s_instance.storageSlotButtons[slot];
}
public void RefreshAll()
{
var host = CECGameRun.Instance?.GetHostPlayer();
var trash = host?.GetTrashBox();
if (trash == null)
return;
int size = trash.GetSize();
EnsureSlotButtons(size);
for (int i = 0; i < storageSlotButtons.Count; i++)
{
var btn = storageSlotButtons[i];
if (btn == null)
continue;
bool active = i < size;
btn.gameObject.SetActive(active);
if (!active)
continue;
btn.onClick.RemoveAllListeners();
int captured = i;
btn.onClick.AddListener(() => OnStorageSlotClicked(captured));
var item = trash.GetItem(i, false);
ApplySlotIcon(btn, item);
EC_SlotStackCountDisplay.UpdateButton(btn, item);
}
RefreshMoney();
}
void RefreshMoney()
{
var host = CECGameRun.Instance?.GetHostPlayer();
if (host == null)
return;
string text = host.GetTrashBoxMoneyCnt().ToString();
foreach (var t in trashMoneyTextsLegacy)
if (t != null) t.text = text;
foreach (var t in trashMoneyTextsTMP)
if (t != null) t.text = text;
}
void EnsureSlotButtons(int required)
{
if (required <= 0 || storageSlotButtons.Count == 0)
return;
var template = storageSlotButtons[0];
var parent = template.transform.parent;
while (storageSlotButtons.Count < required && template != null && parent != null)
{
var clone = Instantiate(template, parent);
storageSlotButtons.Add(clone);
}
}
void ApplySlotIcon(Button btn, EC_IvtrItem item)
{
var image = GetSlotIconImage(btn);
if (image == null)
return;
if (!_defaultSlotSprites.ContainsKey(image))
_defaultSlotSprites[image] = image.sprite;
bool hasItem = item != null && item.m_iCount > 0;
if (hasItem)
{
image.sprite = EC_IvtrItemUtils.Instance.ResolveItemIconSprite(item.m_tid);
image.enabled = true;
image.color = Color.white;
}
else
{
image.sprite = _defaultSlotSprites[image];
image.enabled = true;
}
}
static Image GetSlotIconImage(Button btn)
{
if (btn == null)
return null;
var icon = btn.transform.Find("Icon");
if (icon != null && icon.TryGetComponent<Image>(out var iconImg))
return iconImg;
icon = btn.transform.Find("Img_Icon");
if (icon != null && icon.TryGetComponent<Image>(out iconImg))
return iconImg;
return btn.GetComponent<Image>();
}
void OnStorageSlotClicked(int slot)
{
var host = CECGameRun.Instance?.GetHostPlayer();
if (host == null || !host.IsUsingTrashBox())
return;
_selectedTrashSlot = -1;
var invUi = UnityEngine.Object.FindFirstObjectByType<EC_InventoryUI>();
invUi?.ShowItemDetailForPackage(TrashWhere, slot);
}
void ResolveStorageActionButtons()
{
var buttons = GetComponentsInChildren<Button>(true);
for (int i = 0; i < buttons.Length; i++)
{
var btn = buttons[i];
if (btn == null || btn == closeButton)
continue;
string n = btn.name.ToLowerInvariant();
if (depositMoneyButton == null && (n.Contains("deposit") || n.Contains("put_money") || n.Contains("gui_tien")
|| n.Contains("send_money") || n == "choosemoney" || n.Contains("btn_put")))
depositMoneyButton = btn;
if (withdrawMoneyButton == null && (n.Contains("withdraw") || n.Contains("get_money") || n.Contains("rut")
|| n.Contains("take_money") || n.Contains("btn_get")))
withdrawMoneyButton = btn;
if (sortPackButton == null && (n.Contains("arrange") || n.Contains("sort") || n.Contains("sap_xep")
|| n == "btn_arrange"))
sortPackButton = btn;
}
}
void WireStorageActionButtons()
{
if (depositMoneyButton != null)
{
depositMoneyButton.onClick.RemoveAllListeners();
depositMoneyButton.onClick.AddListener(OnDepositMoneyClicked);
}
if (withdrawMoneyButton != null)
{
withdrawMoneyButton.onClick.RemoveAllListeners();
withdrawMoneyButton.onClick.AddListener(OnWithdrawMoneyClicked);
}
if (sortPackButton != null)
{
sortPackButton.onClick.RemoveAllListeners();
sortPackButton.onClick.AddListener(OnSortPackClicked);
}
}
/// <summary>Gửi tiền vào kho (túi → trash).</summary>
void OnDepositMoneyClicked()
{
var host = CECGameRun.Instance?.GetHostPlayer();
if (host == null || !host.IsUsingTrashBox())
return;
int max = (int)Mathf.Min(int.MaxValue, host.GetMoneyAmount());
if (max <= 0)
return;
DlgQuantity.Show(DlgQuantity.QuantityMode.StorageDepositMoney, max, Mathf.Min(1, max));
}
/// <summary>Rút tiền ra (trash → túi).</summary>
void OnWithdrawMoneyClicked()
{
var host = CECGameRun.Instance?.GetHostPlayer();
if (host == null || !host.IsUsingTrashBox())
return;
int max = host.GetTrashBoxMoneyCnt();
if (max <= 0)
return;
DlgQuantity.Show(DlgQuantity.QuantityMode.StorageWithdrawMoney, max, Mathf.Min(1, max));
}
/// <summary>Sắp xếp ô kho (IVTRTYPE_TRASHBOX). C++ CDlgStorage::OnCommand_arrange on Win_Storage.</summary>
void OnSortPackClicked()
{
var host = CECGameRun.Instance?.GetHostPlayer();
if (host == null || !host.IsUsingTrashBox())
return;
int cool = host.GetCoolTime((int)CoolTimeIndex.GP_CT_MULTI_EXCHANGE_ITEM, out _);
if (cool > 0)
{
EC_Game.GetGameRun()?.AddFixedMessage((int)FixedMsg.FIXMSG_CMD_INCOOLTIME);
return;
}
host.SortPack(TrashWhere);
RefreshAll();
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 54080176355858245aee54b7ee47a66f
@@ -1667,6 +1667,71 @@ namespace CSNetwork.S2CCommand
public ushort size;
public int[] idlist;
};
// PW_CPP: EC_GPDataType.h — trash box C2S payloads
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_get_trashbox_info
{
public byte is_accountbox;
public byte detail;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_exg_trashbox_item_c2s
{
public byte where;
public byte index1;
public byte index2;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_move_trashbox_item_c2s
{
public byte where;
public byte src;
public byte dest;
public uint amount;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_exg_trashbox_ivtr_c2s
{
public byte where;
public byte idx_tra;
public byte idx_inv;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_trashbox_item_to_ivtr_c2s
{
public byte where;
public byte idx_tra;
public byte idx_inv;
public uint amount;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_ivtr_item_to_trashbox_c2s
{
public byte where;
public byte idx_inv;
public byte idx_tra;
public uint amount;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_exg_trashbox_money_c2s
{
public byte is_accountbox;
public uint inv_money;
public uint trashbox_money;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct OpenTrashCONTENT
{
public uint psw_size;
}
}
// Player and NPC state
@@ -4,6 +4,7 @@ using CSNetwork.S2CCommand;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using UnityEngine;
namespace CSNetwork.C2SCommand
@@ -1188,6 +1189,85 @@ namespace CSNetwork.C2SCommand
return SerializeCommand(CommandID.SEVNPC_SERVE, cmd, content);
}
public static Octets CreateCmdGetTrashBoxData(bool detail, bool accountBox = false)
{
var cmd = new cmd_get_trashbox_info
{
detail = (byte)(detail ? 1 : 0),
is_accountbox = (byte)(accountBox ? 1 : 0)
};
return SerializeCommand(CommandID.GET_TRASHBOX_INFO, cmd);
}
public static Octets CreateCmdExgTrashBoxItem(byte where, byte index1, byte index2)
{
var cmd = new cmd_exg_trashbox_item_c2s { where = where, index1 = index1, index2 = index2 };
return SerializeCommand(CommandID.EXG_TRASHBOX_ITEM, cmd);
}
public static Octets CreateCmdMoveTrashBoxItem(byte where, byte src, byte dest, uint amount)
{
var cmd = new cmd_move_trashbox_item_c2s { where = where, src = src, dest = dest, amount = amount };
return SerializeCommand(CommandID.MOVE_TRASHBOX_ITEM, cmd);
}
public static Octets CreateCmdExgTrashBoxIvtrItem(byte where, byte idxTra, byte idxInv)
{
var cmd = new cmd_exg_trashbox_ivtr_c2s { where = where, idx_tra = idxTra, idx_inv = idxInv };
return SerializeCommand(CommandID.EXG_TRASHBOX_IVTR, cmd);
}
public static Octets CreateCmdMoveTrashBoxToIvtr(byte where, byte idxTra, byte idxInv, uint amount)
{
var cmd = new cmd_trashbox_item_to_ivtr_c2s
{
where = where, idx_tra = idxTra, idx_inv = idxInv, amount = amount
};
return SerializeCommand(CommandID.TRASHBOX_ITEM_TO_IVTR, cmd);
}
public static Octets CreateCmdMoveIvtrToTrashBox(byte where, byte idxInv, byte idxTra, uint amount)
{
var cmd = new cmd_ivtr_item_to_trashbox_c2s
{
where = where, idx_inv = idxInv, idx_tra = idxTra, amount = amount
};
return SerializeCommand(CommandID.IVTR_ITEM_TO_TRASHBOX, cmd);
}
public static Octets CreateCmdExgTrashBoxMoney(int invMoney, int trashMoney, bool accountBox = false)
{
var cmd = new cmd_exg_trashbox_money_c2s
{
is_accountbox = (byte)(accountBox ? 1 : 0),
inv_money = (uint)invMoney,
trashbox_money = (uint)trashMoney
};
return SerializeCommand(CommandID.EXG_TRASHBOX_MONEY, cmd);
}
public static Octets CreateNPCSevOpenTrashCmd(string password)
{
uint passwdSize = 0;
byte[] passwordBytes = null;
if (!string.IsNullOrEmpty(password))
{
passwordBytes = Encoding.UTF8.GetBytes(password);
passwdSize = (uint)passwordBytes.Length;
}
var cmd = new cmd_sevnpc_serve
{
service_type = NPC_service_type.GP_NPCSEV_OPENTRASH,
len = (uint)Marshal.SizeOf<OpenTrashCONTENT>() + passwdSize
};
var content = new OpenTrashCONTENT { psw_size = passwdSize };
var octets = SerializeCommand(CommandID.SEVNPC_SERVE, cmd, content);
if (passwdSize > 0 && passwordBytes != null)
octets.Insert(octets.Size, passwordBytes);
return octets;
}
public static Octets CreateGetOtherEquipCmd(ushort _size, int[] _idlist)
{
var cmd = new cmd_get_other_equip
@@ -3279,4 +3279,97 @@ namespace CSNetwork.GPDataType
{
public int id;
};
// PW_CPP: EC_GPDataType.h — trash box (warehouse) S2C payloads
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_trashbox_pwd_changed
{
public byte has_passwd;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_trashbox_pwd_state
{
public byte has_passwd;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_trashbox_open
{
public byte is_accountbox;
public ushort slot_size;
public ushort slot_size2;
public ushort slot_size3;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_trashbox_close
{
public byte is_accountbox;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_trashbox_wealth
{
public byte is_accountbox;
public uint money;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_exg_trashbox_item
{
public byte where;
public byte idx1;
public byte idx2;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_move_trashbox_item
{
public byte where;
public byte src;
public byte dest;
public uint amount;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_exg_trashbox_ivtr
{
public byte where;
public byte idx_tra;
public byte idx_inv;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_ivty_item_to_trash
{
public byte where;
public byte src;
public byte dest;
public uint amount;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_trash_item_to_ivtr
{
public byte where;
public byte src;
public byte dest;
public uint amount;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_exg_trash_money
{
public byte is_accountbox;
public int inv_delta;
public int tra_delta;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_trashbox_size
{
public byte where;
public int iNewSize;
}
}
@@ -1416,6 +1416,21 @@ namespace CSNetwork
case CommandID.MINE_GATHERED:
EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERGATHER, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
break;
case CommandID.TRASHBOX_PWD_CHANGED:
case CommandID.TRASHBOX_PWD_STATE:
case CommandID.TRASHBOX_OPEN:
case CommandID.TRASHBOX_CLOSE:
case CommandID.TRASHBOX_WEALTH:
case CommandID.EXG_TRASH_MONEY:
case CommandID.EXG_TRASHBOX_ITEM:
case CommandID.MOVE_TRASHBOX_ITEM:
case CommandID.EXG_TRASHBOX_IVTR:
case CommandID.IVTR_ITEM_TO_TRASH:
case CommandID.TRASH_ITEM_TO_IVTR:
case CommandID.TRASHBOX_SIZE:
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_TRASHBOXOP, (int)MANAGER_INDEX.MAN_PLAYER, 0,
pDataBuf, pCmdHeader);
break;
case CommandID.COOLTIME_DATA:
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_COOLTIMEDATA, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
break;
@@ -3102,5 +3117,53 @@ namespace CSNetwork
// TODO: C2SCommandFactory.CreateNPCSevCrossServerGetOutCmd() and SendProtocol
}
public void c2s_CmdGetTrashBoxData(bool detail, bool accountBox = false)
{
var req = new gamedatasend { Data = C2SCommandFactory.CreateCmdGetTrashBoxData(detail, accountBox) };
SendProtocol(req);
}
public void c2s_CmdNPCSevOpenTrash(string password)
{
var req = new gamedatasend { Data = C2SCommandFactory.CreateNPCSevOpenTrashCmd(password ?? "") };
SendProtocol(req);
}
public void c2s_CmdExgTrashBoxItem(byte where, byte index1, byte index2)
{
var req = new gamedatasend { Data = C2SCommandFactory.CreateCmdExgTrashBoxItem(where, index1, index2) };
SendProtocol(req);
}
public void c2s_CmdMoveTrashBoxItem(byte where, byte src, byte dest, uint amount)
{
var req = new gamedatasend { Data = C2SCommandFactory.CreateCmdMoveTrashBoxItem(where, src, dest, amount) };
SendProtocol(req);
}
public void c2s_CmdExgTrashBoxIvtrItem(byte where, byte idxTra, byte idxInv)
{
var req = new gamedatasend { Data = C2SCommandFactory.CreateCmdExgTrashBoxIvtrItem(where, idxTra, idxInv) };
SendProtocol(req);
}
public void c2s_CmdMoveTrashBoxToIvtr(byte where, byte idxTra, byte idxInv, uint amount)
{
var req = new gamedatasend { Data = C2SCommandFactory.CreateCmdMoveTrashBoxToIvtr(where, idxTra, idxInv, amount) };
SendProtocol(req);
}
public void c2s_CmdMoveIvtrToTrashBox(byte where, byte idxInv, byte idxTra, uint amount)
{
var req = new gamedatasend { Data = C2SCommandFactory.CreateCmdMoveIvtrToTrashBox(where, idxInv, idxTra, amount) };
SendProtocol(req);
}
public void c2s_CmdExgTrashBoxMoney(int invMoney, int trashMoney, bool accountBox = false)
{
var req = new gamedatasend { Data = C2SCommandFactory.CreateCmdExgTrashBoxMoney(invMoney, trashMoney, accountBox) };
SendProtocol(req);
}
}
}
@@ -654,41 +654,6 @@ namespace BrewMonster.Network
{
Instance._gameSession.c2s_SendCmdStandUp();
}
public static void c2s_CmdSitDown()
{
Instance._gameSession.c2s_SendCmdSitDown();
}
public static void c2s_CmdTeamAssistSel(int idTeamMember)
{
Instance._gameSession.c2s_SendCmdTeamAssistSel(idTeamMember);
}
public static void c2s_CmdActiveRushFly(bool bActive)
{
Instance._gameSession.c2s_SendCmdActiveRushFly(bActive);
}
public static void c2s_CmdOpenBoothTest()
{
Instance._gameSession.c2s_SendCmdOpenBoothTest();
}
public static void c2s_CmdBindPlayerRequest(int idTarget)
{
Instance._gameSession.c2s_SendCmdBindPlayerRequest(idTarget);
}
public static void c2s_CmdBindPlayerInvite(int idTarget)
{
Instance._gameSession.c2s_SendCmdBindPlayerInvite(idTarget);
}
public static bool trade_Start(int idTarget)
{
return Instance._gameSession.trade_Start(idTarget);
}
#region Task
public static void c2s_CmdGetAllData(bool byPack, bool byEquip, bool byTask)
{
@@ -973,6 +938,81 @@ namespace BrewMonster.Network
{
Instance._gameSession.c2s_SendCmdNPCSevRestorePet(iPetIdx);
}
public static void c2s_CmdSitDown()
{
Instance._gameSession.c2s_SendCmdSitDown();
}
public static void c2s_CmdTeamAssistSel(int idTeamMember)
{
Instance._gameSession.c2s_SendCmdTeamAssistSel(idTeamMember);
}
public static void trade_Start(int idTarget)
{
Instance._gameSession.trade_Start(idTarget);
}
public static void c2s_CmdOpenBoothTest()
{
Instance._gameSession.c2s_SendCmdOpenBoothTest();
}
public static void c2s_CmdActiveRushFly(bool bActive)
{
Instance._gameSession.c2s_SendCmdActiveRushFly(bActive);
}
public static void c2s_CmdBindPlayerInvite(int idTarget)
{
Instance._gameSession.c2s_SendCmdBindPlayerInvite(idTarget);
}
public static void c2s_CmdBindPlayerRequest(int idTarget)
{
Instance._gameSession.c2s_SendCmdBindPlayerRequest(idTarget);
}
public static void c2s_CmdNPCSevOpenTrash(string password)
{
Instance._gameSession.c2s_CmdNPCSevOpenTrash(password);
}
public static void c2s_CmdGetTrashBoxData(bool detail, bool accountBox = false)
{
Instance._gameSession.c2s_CmdGetTrashBoxData(detail, accountBox);
}
public static void c2s_CmdExgTrashBoxItem(byte where, byte index1, byte index2)
{
Instance._gameSession.c2s_CmdExgTrashBoxItem(where, index1, index2);
}
public static void c2s_CmdMoveTrashBoxItem(byte where, byte src, byte dest, uint amount)
{
Instance._gameSession.c2s_CmdMoveTrashBoxItem(where, src, dest, amount);
}
public static void c2s_CmdExgTrashBoxIvtrItem(byte where, byte idxTra, byte idxInv)
{
Instance._gameSession.c2s_CmdExgTrashBoxIvtrItem(where, idxTra, idxInv);
}
public static void c2s_CmdMoveTrashBoxToIvtr(byte where, byte idxTra, byte idxInv, uint amount)
{
Instance._gameSession.c2s_CmdMoveTrashBoxToIvtr(where, idxTra, idxInv, amount);
}
public static void c2s_CmdMoveIvtrToTrashBox(byte where, byte idxInv, byte idxTra, uint amount)
{
Instance._gameSession.c2s_CmdMoveIvtrToTrashBox(where, idxInv, idxTra, amount);
}
public static void c2s_CmdExgTrashBoxMoney(int invMoney, int trashMoney, bool accountBox = false)
{
Instance._gameSession.c2s_CmdExgTrashBoxMoney(invMoney, trashMoney, accountBox);
}
}
/// <summary>
+20 -2
View File
@@ -326,7 +326,10 @@ namespace BrewMonster.UI
return null;
}
var prefab = m_dialogResouce.GetPrefabDialog(pszName);
if(prefab != null)
if (prefab == null)
prefab = TryLoadDialogPrefabFallback(pszName);
if (prefab != null)
{
var instance = GameObject.Instantiate(prefab, m_canvas.transform);
var dialog = instance.GetComponent<AUIDialog>();
@@ -336,10 +339,11 @@ namespace BrewMonster.UI
m_DlgName[pszName] = dialog;
return dialog;
}
BMLogger.LogError($"[AUIManager] Prefab '{pszName}' has no AUIDialog component on root.");
}
else
{
BMLogger.LogError($" Không tìm thấy prefab chứa dialog {pszName} ");
BMLogger.LogError($"[AUIManager] Không tìm thấy prefab dialog '{pszName}'. Add id+prefab to DialogScriptTableObject (Resources/UI/DialogScriptTableObject.asset).");
}
}
@@ -389,5 +393,19 @@ namespace BrewMonster.UI
return true;
}
/// <summary>
/// Runtime fallback when a dialog exists as prefab but is missing from DialogScriptTableObject.
/// </summary>
static GameObject TryLoadDialogPrefabFallback(string pszName)
{
if (string.IsNullOrEmpty(pszName))
return null;
#if UNITY_EDITOR
return UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>($"Assets/Prefabs/UI/{pszName}.prefab");
#else
return null;
#endif
}
}
}
@@ -2771,8 +2771,9 @@ namespace BrewMonster.UI
}
else if (iService == CDLGNPC.CDLGNPC_VIEW_TRASHBOX && (pEssence?.combined_services & 0x40000000) != 0)
{
//GetGameSession().c2s_CmdNPCSevViewTrash();
//CloseDialogue();
UnityGameSession.c2s_CmdGetTrashBoxData(true);
CloseDialogue();
GetGameUIMan().EndNPCService();
return;
}
else if (iService == CDLGNPC.CDLGNPC_VIEW_DPS_DPH_RANK && (pEssence?.combined_services & 0x80000000) != 0)
@@ -3258,12 +3259,11 @@ namespace BrewMonster.UI
// id_dialog = (int)pService.id_dialog;
// idFunction = (int)SERVICE_TYPE.NPC_PROXY;
// }
/*else if (DataType == DATA_TYPE.DT_NPC_STORAGE_SERVICE)
else if (DataType == DATA_TYPE.DT_NPC_STORAGE_SERVICE)
{
NPC_STORAGE_SERVICE pService = (NPC_STORAGE_SERVICE)pData;
string strText1 = m_pLst_Main.GetText(nCurSel);
string strComp1 = (GetStringFromTable(249)) + (GetStringFromTable(253));
string strComp2 = (GetStringFromTable(249)) + (GetStringFromTable(8080));
string strComp1 = GetStringFromTable(249) + GetStringFromTable(253);
string strComp2 = GetStringFromTable(249) + GetStringFromTable(8080);
if (string.Equals(strText1, strComp1, StringComparison.OrdinalIgnoreCase))
idFunction = (int)SERVICE_TYPE.NPC_STORAGE_PASSWORD;
@@ -3271,7 +3271,7 @@ namespace BrewMonster.UI
idFunction = (int)SERVICE_TYPE.NPC_ACCOUNT_STORAGE;
else
idFunction = (int)SERVICE_TYPE.NPC_STORAGE;
}*/
}
else if (DataType == DATA_TYPE.DT_NPC_MAKE_SERVICE)
{
NPC_MAKE_SERVICE pService = (NPC_MAKE_SERVICE)pData;
@@ -3765,13 +3765,16 @@ namespace BrewMonster.UI
}
else if (idFunction == (int)SERVICE_TYPE.NPC_STORAGE)
{
//if (GetHostPlayer().TrashBoxHasPsw())
//{
// pShow1 = m_pAUIManager.GetDialog("Win_InputString");
// pShow1.GetDlgItem("DEFAULT_Txt_Input").SetText(_AL(""));
//}
//else
// g_pGame.GetGameSession().c2s_CmdNPCSevOpenTrash("");
if (GetHostPlayer().TrashBoxHasPsw())
{
dialogue1 = "Win_InputString";
// PW_TODO: password input → c2s_CmdNPCSevOpenTrash(password)
}
else
{
UnityGameSession.c2s_CmdNPCSevOpenTrash("");
GetGameUIMan().EndNPCService();
}
}
else if (idFunction == (int)SERVICE_TYPE.NPC_STORAGE_PASSWORD)
{
@@ -0,0 +1,251 @@
using BrewMonster.Network;
using BrewMonster.Scripts;
using BrewMonster.Scripts.Managers;
using System;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace BrewMonster.UI
{
/// <summary>
/// Numeric input dialog (C++ CDlgInputNO / DlgInputNO). Dialog id: <c>DlgQuantity</c>.
/// </summary>
public class DlgQuantity : AUIDialog
{
public enum QuantityMode
{
None = 0,
/// <summary>Gửi tiền vào kho — C++ INPUTNO_STORAGE_IVTR_MONEY (Win_Inventory choosemoney + storage open).</summary>
StorageDepositMoney,
/// <summary>Rút tiền ra — C++ INPUTNO_STORAGE_TRASH_MONEY (Win_Storage choosemoney).</summary>
StorageWithdrawMoney,
}
[Header("Amount")]
[SerializeField] private TMP_InputField amountInput;
[SerializeField] private Text amountTextLegacy;
[Header("Buttons (C++: confirm / IDCANCEL / add / minus / max)")]
[SerializeField] private Button confirmButton;
[SerializeField] private Button cancelButton;
[SerializeField] private Button addButton;
[SerializeField] private Button minusButton;
[SerializeField] private Button maxButton;
private QuantityMode _mode = QuantityMode.None;
private int _min = 1;
private int _max = 1;
private int _current = 1;
private Action<int, QuantityMode> _onConfirmed;
public static DlgQuantity GetDialog()
{
return EC_Game.GetGameRun()?.GetUIManager()?.GetInGameUIMan()?.GetDialog("DlgQuantity") as DlgQuantity;
}
/// <summary>Open quantity panel; on confirm invokes callback then sends C2S when mode is storage money.</summary>
public static void Show(QuantityMode mode, int maxAmount, int defaultAmount = 1, Action<int, QuantityMode> onConfirmed = null)
{
var dlg = GetDialog();
if (dlg == null)
{
Debug.LogError("[DlgQuantity] Dialog 'DlgQuantity' not found. Add prefab to DialogScriptTableObject (id DlgQuantity).");
return;
}
CECUIManager.Instance?.ShowDialogOverlay("DlgQuantity");
dlg.Open(mode, maxAmount, defaultAmount, onConfirmed);
}
public override void Awake()
{
base.Awake();
ResolveControls();
WireButtons();
}
void ResolveControls()
{
if (amountInput == null)
{
amountInput = GetComponentInChildren<TMP_InputField>(true);
if (amountInput != null && amountInput.name.Contains("DEFAULT", StringComparison.OrdinalIgnoreCase) == false)
{
var all = GetComponentsInChildren<TMP_InputField>(true);
for (int i = 0; i < all.Length; i++)
{
if (all[i] != null && all[i].name.IndexOf("Txt_No", StringComparison.OrdinalIgnoreCase) >= 0)
{
amountInput = all[i];
break;
}
}
}
}
ResolveButton(ref confirmButton, "confirm", "btn_confirm", "ok");
ResolveButton(ref cancelButton, "cancel", "idcanel", "btn_cancel", "close");
ResolveButton(ref addButton, "add", "btn_add", "incre", "plus");
ResolveButton(ref minusButton, "minus", "btn_minus", "reduce", "decre");
ResolveButton(ref maxButton, "max", "btn_max");
}
void ResolveButton(ref Button field, params string[] nameHints)
{
if (field != null)
return;
var buttons = GetComponentsInChildren<Button>(true);
for (int i = 0; i < buttons.Length; i++)
{
var btn = buttons[i];
if (btn == null)
continue;
string n = btn.name.ToLowerInvariant();
for (int h = 0; h < nameHints.Length; h++)
{
if (n.Contains(nameHints[h]))
{
field = btn;
return;
}
}
}
}
void WireButtons()
{
if (confirmButton != null)
{
confirmButton.onClick.RemoveAllListeners();
confirmButton.onClick.AddListener(OnConfirm);
}
if (cancelButton != null)
{
cancelButton.onClick.RemoveAllListeners();
cancelButton.onClick.AddListener(OnCancel);
}
if (addButton != null)
{
addButton.onClick.RemoveAllListeners();
addButton.onClick.AddListener(OnAdd);
}
if (minusButton != null)
{
minusButton.onClick.RemoveAllListeners();
minusButton.onClick.AddListener(OnMinus);
}
if (maxButton != null)
{
maxButton.onClick.RemoveAllListeners();
maxButton.onClick.AddListener(OnMax);
}
if (amountInput != null)
{
amountInput.onValueChanged.RemoveAllListeners();
amountInput.onValueChanged.AddListener(OnAmountInputChanged);
amountInput.contentType = TMP_InputField.ContentType.IntegerNumber;
}
}
public void Open(QuantityMode mode, int maxAmount, int defaultAmount, Action<int, QuantityMode> onConfirmed)
{
_mode = mode;
_onConfirmed = onConfirmed;
_max = Mathf.Max(0, maxAmount);
_min = _max > 0 ? 1 : 0;
_current = Mathf.Clamp(defaultAmount, _min, Mathf.Max(_min, _max));
if (_max <= 0)
_current = 0;
ApplyAmountToUi();
Show(true);
}
void ApplyAmountToUi()
{
string text = _current.ToString();
if (amountInput != null)
amountInput.SetTextWithoutNotify(text);
if (amountTextLegacy != null)
amountTextLegacy.text = text;
}
int ReadAmountFromUi()
{
if (amountInput != null && int.TryParse(amountInput.text, out int v))
return v;
if (amountTextLegacy != null && int.TryParse(amountTextLegacy.text, out v))
return v;
return _current;
}
void SetAmount(int value)
{
if (_max <= 0)
{
_current = 0;
}
else
{
_current = Mathf.Clamp(value, _min, _max);
}
ApplyAmountToUi();
}
void OnAmountInputChanged(string _)
{
SetAmount(ReadAmountFromUi());
}
void OnAdd() => SetAmount(_current + 1);
void OnMinus() => SetAmount(_current - 1);
void OnMax() => SetAmount(_max);
void OnConfirm()
{
if (_max <= 0)
{
OnCancel();
return;
}
int n = ReadAmountFromUi();
if (n < _min)
n = _min;
if (n > _max)
n = _max;
if (n <= 0)
return;
ExecuteMode(n);
_onConfirmed?.Invoke(n, _mode);
Close();
}
void ExecuteMode(int amount)
{
switch (_mode)
{
case QuantityMode.StorageDepositMoney:
// C++ INPUTNO_STORAGE_IVTR_MONEY: ExgTrashBoxMoney(iTrash=0, iIvtr=amount)
UnityGameSession.c2s_CmdExgTrashBoxMoney(amount, 0);
break;
case QuantityMode.StorageWithdrawMoney:
// C++ INPUTNO_STORAGE_TRASH_MONEY: ExgTrashBoxMoney(iTrash=amount, iIvtr=0)
UnityGameSession.c2s_CmdExgTrashBoxMoney(0, amount);
break;
}
}
void OnCancel() => Close();
void Close()
{
_mode = QuantityMode.None;
_onConfirmed = null;
CECUIManager.Instance?.HideDialogOverlay("DlgQuantity");
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: dbd9eed9b75d31f44b114eb840444ae0
@@ -125,6 +125,8 @@ namespace BrewMonster.Scripts.UI
/// </summary>
/// <param name="panel">The GameObject panel to show/hide</param>
/// <param name="show">True to show, false to hide</param>
public const int OverlayPanelSortOrder = 2000;
public static void ShowPanel(GameObject panel, bool show)
{
if (panel == null)
@@ -145,6 +147,25 @@ namespace BrewMonster.Scripts.UI
}
}
/// <summary>Draw panel above other UI dialogs (sibling order + canvas sort).</summary>
public static void BringPanelToFront(GameObject panel)
{
if (panel == null)
return;
panel.transform.SetAsLastSibling();
var canvas = panel.GetComponent<Canvas>();
if (canvas == null)
canvas = panel.AddComponent<Canvas>();
canvas.overrideSorting = true;
canvas.sortingOrder = OverlayPanelSortOrder;
if (panel.GetComponent<GraphicRaycaster>() == null)
panel.AddComponent<GraphicRaycaster>();
}
/// <summary>
/// Helper class for managing dual text component support (Legacy Text + TextMeshPro).
/// Allows UI components to work with both text systems simultaneously.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 11d09ee52b0c5f24fb3ef21e177ebe2d
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 0986049a141406946b0ed97344b84f78
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+3 -2
View File
@@ -12786,6 +12786,7 @@ MonoBehaviour:
tmp: {fileID: 6020258894941961325}
equipButton: {fileID: 472698755110594484}
dropButton: {fileID: 540159372834342487}
storageTransferButton: {fileID: 0}
splitPanelRoot: {fileID: 3772330938303001319}
splitAmountText: {fileID: 3418403519615399396}
splitConfirmButton: {fileID: 7942200720793983179}
@@ -20626,7 +20627,7 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 7209086543831860202, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.y
value: -836.15
value: -42
objectReference: {fileID: 0}
- target: {fileID: 8894405194986632892, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMax.y
@@ -20646,7 +20647,7 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 8894405194986632892, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.y
value: -418.075
value: -21
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
+19 -4
View File
@@ -8,7 +8,7 @@ using CSNetwork.GPDataType;
using CSNetwork.S2CCommand;
using System;
using BrewMonster.UI;
using PerfectWorld.Scripts.Managers;
using BrewMonster.Scripts.Managers;
using UnityEngine;
using static BrewMonster.Scripts.CECHPWork;
using static BrewMonster.Scripts.EC_Inventory;
@@ -88,9 +88,15 @@ namespace BrewMonster
{
UpdateEquipSkins();
}
else if (byPackage == InventoryConst.IVTRTYPE_TRASHBOX && m_bUsingTrashBox)
{
PopupStorageDialog();
}
var ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
ui?.RefreshAll();
if (byPackage == InventoryConst.IVTRTYPE_TRASHBOX)
EC_StorageUI.RefreshAllStatic();
}
break;
}
@@ -131,6 +137,15 @@ namespace BrewMonster
{
UpdateEquipSkins();
}
else if (byPackage == InventoryConst.IVTRTYPE_TRASHBOX && m_bUsingTrashBox)
{
PopupStorageDialog();
}
var uiDetail = GameObject.FindFirstObjectByType<EC_InventoryUI>();
uiDetail?.RefreshAll();
if (byPackage == InventoryConst.IVTRTYPE_TRASHBOX)
EC_StorageUI.RefreshAllStatic();
}
break;
@@ -1598,9 +1613,9 @@ namespace BrewMonster
case Inventory_type.IVTRTYPE_PACK: pInventory = m_pPack; break;
case Inventory_type.IVTRTYPE_EQUIPPACK: pInventory = m_pEquipPack; break;
case Inventory_type.IVTRTYPE_TASKPACK: pInventory = m_pTaskPack; break;
//case Inventory_type.IVTRTYPE_TRASHBOX: pInventory = m_pTrashBoxPack; break;
//case Inventory_type.IVTRTYPE_TRASHBOX2: pInventory = m_pTrashBoxPack2; break;
//case Inventory_type.IVTRTYPE_TRASHBOX3: pInventory = m_pTrashBoxPack3; break;
case Inventory_type.IVTRTYPE_TRASHBOX: pInventory = m_pTrashBoxPack; break;
case Inventory_type.IVTRTYPE_TRASHBOX2: pInventory = m_pTrashBoxPack2; break;
case Inventory_type.IVTRTYPE_TRASHBOX3: pInventory = m_pTrashBoxPack3; break;
//case Inventory_type.IVTRTYPE_ACCOUNT_BOX: pInventory = m_pAccountBoxPack; break;
//case Inventory_type.IVTRTYPE_GENERALCARD_BOX: pInventory = m_pGeneralCardPack; break;
//case IVTRTYPE_PACK_CLIENT_GENERALCAR.IVTRTYPE_CLIENT_GENERALCARD_PACK: pInventory = m_pClientGenCardPack; break;
+328
View File
@@ -0,0 +1,328 @@
using BrewMonster.Managers;
using BrewMonster.Network;
using BrewMonster.Scripts;
using BrewMonster.Scripts.Managers;
using CSNetwork;
using CSNetwork.GPDataType;
using BrewMonster.Scripts.Managers;
using BrewMonster.UI;
using System;
using UnityEngine;
using static BrewMonster.Scripts.EC_Inventory;
namespace BrewMonster
{
/// <summary>
/// Host player warehouse / trash box (C++ CECHostPlayer::OnMsgHstTrashBoxOperation, GetTrashBox*).
/// </summary>
public partial class CECHostPlayer
{
private readonly EC_Inventory m_pTrashBoxPack = new EC_Inventory();
private readonly EC_Inventory m_pTrashBoxPack2 = new EC_Inventory();
private readonly EC_Inventory m_pTrashBoxPack3 = new EC_Inventory();
private bool m_bTrashPsw;
private bool m_bFirstTBOpen = true;
private int m_iTrashBoxMoneyCnt;
public bool TrashBoxHasPsw() => m_bTrashPsw;
public int GetTrashBoxMoneyCnt() => m_iTrashBoxMoneyCnt;
public EC_Inventory GetTrashBox() => m_pTrashBoxPack;
public EC_Inventory GetTrashBox2() => m_pTrashBoxPack2;
public EC_Inventory GetTrashBox3() => m_pTrashBoxPack3;
void InitTrashBoxPacks()
{
if (m_pTrashBoxPack.GetSize() == 0)
m_pTrashBoxPack.Init(InventoryConst.IVTRSIZE_TRASHBOX);
if (m_pTrashBoxPack2.GetSize() == 0)
m_pTrashBoxPack2.Init(0);
if (m_pTrashBoxPack3.GetSize() == 0)
m_pTrashBoxPack3.Init(0);
}
public void OnMsgHstTrashBoxOperation(ECMSG Msg)
{
var data = Msg.dwParam1 as byte[];
if (data == null || data.Length == 0)
return;
int cmd = Convert.ToInt32(Msg.dwParam2);
switch (cmd)
{
case CommandID.TRASHBOX_OPEN:
{
var pCmd = GPDataTypeHelper.FromBytes<cmd_trashbox_open>(data);
if (pCmd.is_accountbox != 0)
break; // PW_TODO: account box UI
m_bUsingTrashBox = true;
InitTrashBoxPacks();
if (m_bFirstTBOpen)
{
m_bFirstTBOpen = false;
UnityGameSession.c2s_CmdGetTrashBoxData(true);
}
else
{
if (m_pTrashBoxPack.GetSize() < pCmd.slot_size)
m_pTrashBoxPack.Resize(pCmd.slot_size);
if (m_pTrashBoxPack2.GetSize() < pCmd.slot_size2)
m_pTrashBoxPack2.Resize(pCmd.slot_size2);
if (m_pTrashBoxPack3.GetSize() < pCmd.slot_size3)
m_pTrashBoxPack3.Resize(pCmd.slot_size3);
PopupStorageDialog();
}
break;
}
case CommandID.TRASHBOX_CLOSE:
{
var pCmd = GPDataTypeHelper.FromBytes<cmd_trashbox_close>(data);
if (pCmd.is_accountbox != 0)
break;
m_bUsingTrashBox = false;
PopupStorageDialog(true);
break;
}
case CommandID.TRASHBOX_WEALTH:
{
var pCmd = GPDataTypeHelper.FromBytes<cmd_trashbox_wealth>(data);
if (pCmd.is_accountbox == 0)
m_iTrashBoxMoneyCnt = (int)pCmd.money;
EC_StorageUI.RefreshMoneyStatic();
break;
}
case CommandID.EXG_TRASH_MONEY:
{
var pCmd = GPDataTypeHelper.FromBytes<cmd_exg_trash_money>(data);
if (pCmd.is_accountbox == 0)
{
AddMoneyAmount(pCmd.inv_delta);
m_iTrashBoxMoneyCnt += pCmd.tra_delta;
}
EC_StorageUI.RefreshMoneyStatic();
var invUi = UnityEngine.Object.FindFirstObjectByType<EC_InventoryUI>();
invUi?.RefreshAll();
break;
}
case CommandID.EXG_TRASHBOX_ITEM:
{
var pCmd = GPDataTypeHelper.FromBytes<cmd_exg_trashbox_item>(data);
var pTrash = GetPack(pCmd.where);
pTrash?.ExchangeItem(pCmd.idx1, pCmd.idx2);
EC_StorageUI.RefreshAllStatic();
break;
}
case CommandID.MOVE_TRASHBOX_ITEM:
{
var pCmd = GPDataTypeHelper.FromBytes<cmd_move_trashbox_item>(data);
var pTrash = GetPack(pCmd.where);
pTrash?.MoveItem(pCmd.src, pCmd.dest, (int)pCmd.amount);
EC_StorageUI.RefreshAllStatic();
break;
}
case CommandID.EXG_TRASHBOX_IVTR:
{
var pCmd = GPDataTypeHelper.FromBytes<cmd_exg_trashbox_ivtr>(data);
var pTrash = GetPack(pCmd.where);
if (pTrash == null || m_pPack == null)
break;
var pItem1 = m_pPack.GetItem(pCmd.idx_inv, true);
var pItem2 = pTrash.GetItem(pCmd.idx_tra, true);
m_pPack.SetItem(pCmd.idx_inv, pItem2);
pTrash.SetItem(pCmd.idx_tra, pItem1);
RefreshStorageAndInventoryUi();
break;
}
case CommandID.IVTR_ITEM_TO_TRASH:
{
var pCmd = GPDataTypeHelper.FromBytes<cmd_ivty_item_to_trash>(data);
var pTrash = GetPack(pCmd.where);
if (pTrash == null || m_pPack == null)
break;
var pItem1 = m_pPack.GetItem(pCmd.src);
var pItem2 = pTrash.GetItem(pCmd.dest);
if (pItem1 == null)
break;
if (pItem2 != null)
{
pItem2.AddAmount((int)pCmd.amount);
}
else
{
pItem2 = EC_IvtrItem.CreateItem(pItem1.m_tid, pItem1.m_expire_date, (int)pCmd.amount);
pTrash.SetItem(pCmd.dest, pItem2);
}
m_pPack.RemoveItem(pCmd.src, (int)pCmd.amount);
RefreshStorageAndInventoryUi();
break;
}
case CommandID.TRASH_ITEM_TO_IVTR:
{
var pCmd = GPDataTypeHelper.FromBytes<cmd_trash_item_to_ivtr>(data);
var pTrash = GetPack(pCmd.where);
if (pTrash == null || m_pPack == null)
break;
var pItem1 = pTrash.GetItem(pCmd.src);
var pItem2 = m_pPack.GetItem(pCmd.dest);
if (pItem1 == null)
break;
if (pItem2 != null)
pItem2.AddAmount((int)pCmd.amount);
else
{
pItem2 = EC_IvtrItem.CreateItem(pItem1.m_tid, pItem1.m_expire_date, (int)pCmd.amount);
m_pPack.SetItem(pCmd.dest, pItem2);
}
pTrash.RemoveItem(pCmd.src, (int)pCmd.amount);
RefreshStorageAndInventoryUi();
break;
}
case CommandID.TRASHBOX_PWD_CHANGED:
case CommandID.TRASHBOX_PWD_STATE:
{
var pCmd = GPDataTypeHelper.FromBytes<cmd_trashbox_pwd_state>(data);
m_bTrashPsw = pCmd.has_passwd > 0;
break;
}
case CommandID.TRASHBOX_SIZE:
{
var pCmd = GPDataTypeHelper.FromBytes<cmd_trashbox_size>(data);
int msg = (int)FixedMsg.FIXMSG_TRASHBOX_EXPAND;
int iSize = pCmd.iNewSize;
if (pCmd.where == Inventory_type.IVTRTYPE_TRASHBOX2)
msg = (int)FixedMsg.FIXMSG_TRASHBOX2_EXPAND;
else if (pCmd.where == Inventory_type.IVTRTYPE_TRASHBOX3)
{
msg = (int)FixedMsg.FIXMSG_TRASHBOX3_EXPAND;
m_pTrashBoxPack3?.Resize(pCmd.iNewSize);
}
else if (pCmd.where == Inventory_type.IVTRTYPE_TRASHBOX)
m_pTrashBoxPack?.Resize(pCmd.iNewSize);
EC_Game.GetGameRun()?.AddFixedMessage(msg, iSize);
EC_StorageUI.RefreshAllStatic();
break;
}
}
}
static void RefreshStorageAndInventoryUi()
{
EC_StorageUI.RefreshAllStatic();
UnityEngine.Object.FindFirstObjectByType<EC_InventoryUI>()?.RefreshAll();
}
/// <summary>C++ CECGameUIMan::PopupStorageDialog — show warehouse + inventory.</summary>
public static void PopupStorageDialog(bool close = false)
{
if (close)
{
var host = EC_Game.GetGameRun()?.GetHostPlayer();
bool wasUsingTrash = host != null && host.IsUsingTrashBox();
if (host != null)
host.m_bUsingTrashBox = false;
EC_StorageUI.ClearSelectionStatic();
var invUi = UnityEngine.Object.FindFirstObjectByType<EC_InventoryUI>(FindObjectsInactive.Include);
invUi?.DismissItemDetail();
CECUIManager.Instance?.HideStorageDialogPair();
if (wasUsingTrash)
UnityGameSession.c2s_CmdCancelAction();
EC_Game.GetGameRun()?.GetUIManager()?.GetInGameUIMan()?.EndNPCService();
return;
}
CECUIManager.Instance?.ShowStorageDialogPair();
var storageDlg = EC_Game.GetGameRun()?.GetUIManager()?.GetInGameUIMan()?.GetDialog("EC_StorageUI") as EC_StorageUI;
storageDlg?.RefreshAll();
var invDlg = UnityEngine.Object.FindFirstObjectByType<EC_InventoryUI>(FindObjectsInactive.Include);
invDlg?.RefreshAll();
}
/// <summary>Transfer between main pack and trash box (C++ CDlgStorage::OnItemDragDrop).</summary>
public bool TransferPackAndTrash(byte trashWhere, int trashSlot, int invSlot, int amount = -1)
{
if (trashWhere != InventoryConst.IVTRTYPE_TRASHBOX)
return false;
var trash = GetPack(trashWhere);
var pack = m_pPack;
if (trash == null || pack == null)
return false;
if (trashSlot < 0 || invSlot < 0)
return false;
var srcInv = pack.GetItem(invSlot);
var dstTrash = trash.GetItem(trashSlot);
if (srcInv != null && dstTrash == null)
{
UnityGameSession.c2s_CmdExgTrashBoxIvtrItem(trashWhere, (byte)trashSlot, (byte)invSlot);
return true;
}
var srcTrash = trash.GetItem(trashSlot);
var dstInv = pack.GetItem(invSlot);
if (srcTrash != null && dstInv == null)
{
UnityGameSession.c2s_CmdExgTrashBoxIvtrItem(trashWhere, (byte)trashSlot, (byte)invSlot);
return true;
}
if (srcInv != null && dstTrash != null &&
srcInv.m_tid == dstTrash.m_tid && srcInv.GetPileLimitInstance() > 1)
{
int moveAmt = amount > 0 ? amount : Math.Min(srcInv.m_iCount,
dstTrash.GetPileLimitInstance() - dstTrash.m_iCount);
if (moveAmt > 0)
{
UnityGameSession.c2s_CmdMoveIvtrToTrashBox(trashWhere, (byte)invSlot, (byte)trashSlot, (uint)moveAmt);
return true;
}
}
if (srcTrash != null && dstInv != null &&
srcTrash.m_tid == dstInv.m_tid && srcTrash.GetPileLimitInstance() > 1)
{
int moveAmt = amount > 0 ? amount : Math.Min(srcTrash.m_iCount,
dstInv.GetPileLimitInstance() - dstInv.m_iCount);
if (moveAmt > 0)
{
UnityGameSession.c2s_CmdMoveTrashBoxToIvtr(trashWhere, (byte)trashSlot, (byte)invSlot, (uint)moveAmt);
return true;
}
}
if (srcInv != null || srcTrash != null)
{
UnityGameSession.c2s_CmdExgTrashBoxIvtrItem(trashWhere, (byte)trashSlot, (byte)invSlot);
return true;
}
return false;
}
public bool TransferWithinTrash(byte where, int slotA, int slotB)
{
var trash = GetPack(where);
if (trash == null || slotA < 0 || slotB < 0 || slotA == slotB)
return false;
var a = trash.GetItem(slotA);
var b = trash.GetItem(slotB);
if (a != null && b != null && a.m_tid == b.m_tid && a.GetPileLimitInstance() > 1)
{
int moveAmt = Math.Min(a.m_iCount, b.GetPileLimitInstance() - b.m_iCount);
if (moveAmt > 0)
{
UnityGameSession.c2s_CmdMoveTrashBoxItem(where, (byte)slotA, (byte)slotB, (uint)moveAmt);
return true;
}
}
UnityGameSession.c2s_CmdExgTrashBoxItem(where, (byte)slotA, (byte)slotB);
return true;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f43b17fb174c9ce4bbcdf1eb7637aa91
+7 -1
View File
@@ -139,7 +139,13 @@ namespace BrewMonster
// }
// }
if (m_bUsingTrashBox || DoingSessionPose())
if (m_bUsingTrashBox)
{
CECHostPlayer.PopupStorageDialog(true);
return;
}
if (DoingSessionPose())
{
UnityGameSession.c2s_CmdCancelAction();
return;
+12 -2
View File
@@ -321,6 +321,12 @@ namespace BrewMonster
return m_pEquipPack;
case InventoryConst.IVTRTYPE_TASKPACK:
return m_pTaskPack;
case InventoryConst.IVTRTYPE_TRASHBOX:
return m_pTrashBoxPack;
case InventoryConst.IVTRTYPE_TRASHBOX2:
return m_pTrashBoxPack2;
case InventoryConst.IVTRTYPE_TRASHBOX3:
return m_pTrashBoxPack3;
default:
return null;
}
@@ -329,6 +335,7 @@ namespace BrewMonster
private void Awake()
{
base.Awake();
InitTrashBoxPacks();
m_MoveCtrl = new CECHostMove(this);
// Cache: không bắt buộc, nhưng gọn tay và ít gọi property lặp.
@@ -511,6 +518,9 @@ namespace BrewMonster
case EC_MsgDef.MSG_HST_ITEMOPERATION:
OnMsgHstItemOperation(Msg);
break;
case EC_MsgDef.MSG_HST_TRASHBOXOP:
OnMsgHstTrashBoxOperation(Msg);
break;
case EC_MsgDef.MSG_HST_PICKUPITEM:
OnMsgHstPickupItem(Msg);
break;
@@ -3088,7 +3098,7 @@ namespace BrewMonster
}
// Is host player open trash box ?
bool IsUsingTrashBox()
public bool IsUsingTrashBox()
{
return m_bUsingTrashBox;
}
@@ -3652,7 +3662,7 @@ namespace BrewMonster
m_PetOptCnt.IncCounter(iRealTime);
// Bind command cool counter
if (m_BindCmdCoolCnt.IncCounter(iRealTime))
if (m_BindCmdCoolCnt.IncCounter(iRealTime))
m_BindCmdCoolCnt.Reset(true);
/*
+82 -1
View File
@@ -38,6 +38,8 @@ public class CECUIManager : MonoSingleton<CECUIManager>
[SerializeField] private DialogScriptTableObject dialogResouce;
[SerializeField] private Canvas canvasDlg;
public Transform DialogCanvasTransform => canvasDlg != null ? canvasDlg.transform : null;
[SerializeField]
[Tooltip("Chat TMP: EmotionLibrarySpriteMap (SO). CECGameUIMan is not a MonoBehaviour — assign here on CECUIManager.")]
private EmotionLibrarySpriteMap _emotionLibrarySpriteMap;
@@ -248,6 +250,34 @@ public class CECUIManager : MonoSingleton<CECUIManager>
return Push(componentName);
}
/// <summary>
/// Show a modal on top without hiding stack dialogs (e.g. DlgQuantity over storage + inventory).
/// </summary>
public AUIDialog ShowDialogOverlay(string componentName)
{
if (string.IsNullOrEmpty(componentName) || canvasDlg == null)
return null;
var dlg = GetInGameUIMan()?.GetDialog(componentName);
if (dlg == null)
return null;
dlg.Show(true);
dlg.transform.SetAsLastSibling();
EC_UIUtility.BringPanelToFront(dlg.gameObject);
return dlg;
}
public void HideDialogOverlay(string componentName)
{
if (string.IsNullOrEmpty(componentName))
return;
var dlg = GetInGameUIMan()?.GetDialog(componentName);
if (dlg != null)
dlg.Show(false);
}
public void HideCurrentUIInStack()
{
Pop();
@@ -280,6 +310,57 @@ public class CECUIManager : MonoSingleton<CECUIManager>
return dlg;
}
/// <summary>
/// Show warehouse + inventory together without hiding the first dialog (C++ PopupStorageDialog).
/// </summary>
public void ShowStorageDialogPair()
{
if (canvasDlg == null)
return;
var gui = GetInGameUIMan();
if (gui == null)
return;
var storageDlg = gui.GetDialog("EC_StorageUI");
var invDlg = gui.GetDialog("Win_Inventory");
if (storageDlg == null || invDlg == null)
return;
_uiStack.Remove("Win_Inventory");
_uiStack.Remove("EC_StorageUI");
invDlg.Show(true);
storageDlg.Show(true);
_uiStack.Insert(0, "Win_Inventory");
_uiStack.Insert(0, "EC_StorageUI");
// Inventory below, storage on top (detail panel uses EC_UIUtility overlay sort).
invDlg.transform.SetAsLastSibling();
storageDlg.transform.SetAsLastSibling();
}
/// <summary>Hide warehouse pair and restore previous stack top if any.</summary>
public void HideStorageDialogPair()
{
_uiStack.Remove("EC_StorageUI");
_uiStack.Remove("Win_Inventory");
var gui = GetInGameUIMan();
gui?.GetDialog("Win_Inventory")?.Show(false);
gui?.GetDialog("EC_StorageUI")?.Show(false);
if (_uiStack.Count > 0)
{
var newTop = gui?.GetDialog(_uiStack[0]);
if (newTop != null)
{
newTop.Show(true);
newTop.transform.SetAsLastSibling();
}
}
}
/// <summary>
/// Pop the top dialog off the stack: hide it and bring the new top (if any) to front. Returns true if something was popped.
/// </summary>
@@ -795,7 +876,7 @@ public class CECUIManager : MonoSingleton<CECUIManager>
}
public void GetIconStateMgr()
{
}
/// <summary>Shows the player options menu for the given character. Requires a prefab with id "DlgPlayerOptions" in DialogScriptTableObject.</summary>