using System; using BrewMonster.Common; using BrewMonster.Network; using BrewMonster.Scripts.Managers; using BrewMonster.Scripts.Task; using BrewMonster.UI; using PerfectWorld.Scripts.Managers; using PerfectWorld.Scripts.Task; using UnityEngine; using UnityEngine.UI; using BrewMonster.Scripts.UI; namespace BrewMonster.Scripts.UI { public class CDlgAward : AUIDialog { #region FIELDS AND PROPERTIES private const int MIN_ROW_COUNT_TO_SHOW = 6; [SerializeField] private Button m_pBtn_Confirm; [SerializeField] private Button _btnClose; [SerializeField] private Vector2Int _itemSize = new Vector2Int(50, 50); [SerializeField] private AwardItem _awardItemPrefab; [SerializeField] private Transform _itemsParent; [SerializeField] private Toggle[] _radioButtons; [Header("ItemDetail")] [SerializeField] GameObject itemInfoPanel; [SerializeField] Vector2 itemInfoPanelOffset = new Vector2(20f, 0f); [SerializeField] private TextOutlet descriptionOutlet; [Header("DEBUG")] [SerializeField] private AwardItem[] _awardItems; bool m_bThroughNPC; bool m_bSpendContribution; #endregion int _rowCount => _itemSize.x; int _columnCount => _itemSize.y; #region UNITY METHODS void Awake() { SpawnItems(); InitRadioBtns(); // this is copy paste from InventoryUI.cs. so it have equip and drop button. // temporary disable them itemInfoPanel.SetActive(false); itemInfoPanel.transform.GetChild(0).gameObject.SetActive(false); m_pBtn_Confirm.onClick.AddListener(OnCommand_confirm); } public override void OnEnable() { base.OnEnable(); // Refresh to toggle state. foreach (var toggle in _radioButtons) { toggle.isOn = false; } if (itemInfoPanel != null) { itemInfoPanel.SetActive(false); } if (descriptionOutlet != null) { descriptionOutlet.Set(string.Empty); } _btnClose.onClick.AddListener(OnCloseDlgAward); } public override void OnDisable() { base.OnDisable(); _btnClose.onClick.RemoveListener(OnCloseDlgAward); } private void OnCloseDlgAward() { if (m_bThroughNPC) GetGameUIMan().EndNPCService(); CloseDialogue(); } private void InitRadioBtns() { for (int i = 0; i < _radioButtons.Length; i++) { _radioButtons[i].isOn = false; _radioButtons[i].onValueChanged.AddListener( OnCommand_select ); } } private void SpawnItems() { for (int i = 1; i <= _rowCount; i++) { for (int j = 1; j <= _columnCount; j++) { AwardItem item = Instantiate(_awardItemPrefab ,_itemsParent); item.SetPositionIndex(i, j); // Positioning logic can be added here } } _awardItems = new AwardItem[_rowCount]; _awardItems = GetComponentsInChildren(); _awardItemPrefab.gameObject.SetActive(false); } #endregion protected bool OnInitDialog() { m_bThroughNPC = false; m_bSpendContribution = false; return true; } public void UpdateAwardItem(ushort idTask, bool throughNPC, bool spendContribution = false) { AWARD_DATA ad = new AWARD_DATA(); CECTaskInterface pTask = GetHostPlayer().GetTaskInterface(); m_bThroughNPC = throughNPC; m_bSpendContribution = spendContribution; pTask.GetAwardCandidates(idTask, ref ad); if (ad.m_ulCandItems > 1) { string szName = ""; string strFile = ""; string szText = ""; EC_IvtrItem pItem; // AUICTranslate trans; // TODO // int i, j, idItem, nNum; uint idItem, nNum; // Image pImage; // Image pImage2; AwardItem pImage; // Using AwardItem instead of Image SetData(idTask, ""); // CheckRadioButton(1, 0); // TODO: maybe dont need this m_pBtn_Confirm.gameObject.SetActive(false); for (int i=1; i <= _rowCount; i++) { // sprintf(out szName, "Item_%02d01", i); // pImage = (PAUIIMAGEPICTURE)GetDlgItem(szName); // if (!pImage) break; // All done. // TODO: Show/Hide radio button // sprintf(out szName, "Rdo_Award%d", i); // GetDlgItem(szName)->Show(false); // _radioButtons[i-1].gameObject.SetActive(false); GetToggleBtnAt(i).gameObject.SetActive(false); // for (j = 1;; j++) for (int j=1; j < _columnCount; j++) { // sprintf(out szName, "Item_%02d%02d", i, j); // pImage = (PAUIIMAGEPICTURE)GetDlgItem(szName); // if (!pImage) break; // Line done. pImage = GetAwardItemAt(i, j); if(!pImage) break; if (i - 1 < (int)ad.m_ulCandItems && j - 1 < (int)ad.m_CandItems[i - 1].m_ulAwardItems) { idItem = ad.m_CandItems[i - 1].m_AwardItems[j - 1].m_ulItemTemplId; pItem = EC_IvtrItem.CreateItem((int)idItem, 0, 1); nNum = ad.m_CandItems[i - 1].m_AwardItems[j - 1].m_ulItemNum; if (nNum > 1) { a_sprintf(out szText, _AL("%d"), nNum); pImage.SetText(szText); } else pImage.SetText(_AL("")); pItem.GetDetailDataFromLocal(); string strDesc = pItem.GetDesc(); pImage.SetHint(strDesc); // TODO pImage.SetOnClick(OnAwardItemClicked); // af_GetFileTitle(pItem.GetIconFile(), strFile); // TODO strFile.ToLower(); // TODO: Set the image cover here // pImage.SetCover(GetGameUIMan().m_pA2DSpriteIcons[CECGameUIMan::ICONS_INVENTORY], // GetGameUIMan().m_IconMap[CECGameUIMan::ICONS_INVENTORY][strFile]); var sprite = EC_IvtrItemUtils.Instance.ResolveItemIconSprite(pItem.m_tid); pImage.SetImage(sprite); EC_IvtrEquip pEquip = (EC_IvtrEquip)EC_IvtrItem.CreateItem(pItem.m_tid, pItem.m_expire_date, pItem.m_iCount); pImage.SetColor( (pItem.IsEquipment() && pEquip.IsDestroying()) ? new Color32(128, 128, 128, 255) : new Color32(255, 255, 255, 255)); // delete pItem; pItem = null; // sprintf(out szName, "Rdo_Award%d", i); // GetDlgItem(szName)->Show(true); GetToggleBtnAt(i).gameObject.SetActive(true); } else { pImage.ClearCover(); pImage.SetHint(_AL("")); pImage.SetText(_AL("")); } } } // turn off uneccessary rows if (ad.m_ulCandItems <= MIN_ROW_COUNT_TO_SHOW) { // ����������٣�������ʣ���� // for (i = MIN_ROW_COUNT_TO_SHOW + 1;; i++) for (int i = MIN_ROW_COUNT_TO_SHOW + 1; i < _rowCount; i++) { // sprintf(out szName, "Item_%02d01", i); // pImage = (PAUIIMAGEPICTURE)GetDlgItem(szName); // if (!pImage) break; // All done. pImage = GetAwardItemAt(i, 1); if (!pImage) break; // sprintf(out szName, "Rdo_Award%d", i); // GetDlgItem(szName)->Show(false); GetToggleBtnAt(i).gameObject.SetActive(false); for (int j = 1; j < _columnCount; j++) { // sprintf(out szName, "Item_%02d%02d", i, j); // pImage = (PAUIIMAGEPICTURE)GetDlgItem(szName); // if (!pImage) break; // pImage->Show(false); pImage = GetAwardItemAt(i, j); pImage.Show(false); // pImage2 = (PAUIIMAGEPICTURE)GetDlgItem(AString().Format("Img_%02d%02d", i, j)); // pImage2->Show(false); // pImage2 = GetAwardItemAt(i, j); } } // ����ȷ�ϰ�ť��λ�ã����öԻ����С // TODO: Thiết lập vị trí của nút xác nhận, thiết lập kích thước của hộp thoại. // var pBtnConfirm = m_pBtn_Confirm; // AUIObject* pImgLineMid = GetDlgItem(AString().Format("Item_%02d01", MIN_ROW_COUNT_TO_SHOW)); // AUIObject* pEdiBg = GetDlgItem("background"); // pBtnConfirm->SetPos(pBtnConfirm->GetPos(true).x, // pImgLineMid->GetPos(true).y + pImgLineMid->GetSize().cy + 10); // SetSize(GetSize().cx, pBtnConfirm->GetPos(true).y + pBtnConfirm->GetSize().cy + 30); // pEdiBg->SetSize(pEdiBg->GetSize().cx, GetSize().cy - pEdiBg->GetPos(true).y - 25); } else { // ������ʾȫ���� / Show all rows for (int i = MIN_ROW_COUNT_TO_SHOW + 1; i < _rowCount; i++) { // sprintf(out szName, "Item_%02d01", i); // pImage = (PAUIIMAGEPICTURE)GetDlgItem(szName); // if (!pImage) break; // All done. pImage = GetAwardItemAt(i, 1); if (!pImage) break; // All done. if (ad.m_ulCandItems >= i) { // sprintf(out szName, "Rdo_Award%d", i); // GetDlgItem(szName)->Show(true); GetToggleBtnAt(i).gameObject.SetActive(true); } for (int j = 1; j < _columnCount; j++) { // sprintf(out szName, "Item_%02d%02d", i, j); // pImage = (PAUIIMAGEPICTURE)GetDlgItem(szName); // if (!pImage) break; pImage = GetAwardItemAt(i, j); if (!pImage) break; pImage.Show(true); // pImage2 = (PAUIIMAGEPICTURE)GetDlgItem(AString().Format("Img_%02d%02d", i, j)); // pImage2->Show(true); // TODO: Handle pImage2 if needed // AwardItem pImage2 = GetAwardItemAt(i, j); // pImage2?.Show(true); } } // ����ȷ�ϰ�ť��λ�ã����öԻ����С // TODO: Thiết lập vị trí của nút xác nhận, thiết lập kích thước của hộp thoại. // AUIObject* pBtnConfirm = GetDlgItem("Btn_Confirm"); // AUIObject* pImgLineLast = GetDlgItem(AString().Format("Item_%02d01", i - 1)); // AUIObject* pEdiBg = GetDlgItem("background"); // pBtnConfirm->SetPos(pBtnConfirm->GetPos(true).x, // pImgLineLast->GetPos(true).y + pImgLineLast->GetSize().cy + 10); // SetSize(GetSize().cx, pBtnConfirm->GetPos(true).y + pBtnConfirm->GetSize().cy + 30); // pEdiBg->SetSize(pEdiBg->GetSize().cx, GetSize().cy - pEdiBg->GetPos(true).y - 25); } } } /// /// Debug: equipment slot template ids only; rebuild with + /// for local prop/description testing. /// public void TestItemInInventory() { m_bThroughNPC = false; m_bSpendContribution = false; if (m_pBtn_Confirm != null) m_pBtn_Confirm.gameObject.SetActive(false); for (int r = 1; r <= _rowCount; r++) GetToggleBtnAt(r)?.gameObject.SetActive(false); var host = GetHostPlayer(); var inv = host?.GetInventory(InventoryConst.IVTRTYPE_EQUIPPACK); if (inv == null) { ClearAwardGridForLocalEquipmentTest(0); return; } int capacity = _rowCount * _columnCount; int filledCount = 0; for (int slot = 0; slot < inv.GetSize(); slot++) { var eq = inv.GetItem(slot, false); if (eq == null) continue; int tid = eq.GetTemplateID(); if (tid <= 0) continue; if (filledCount >= capacity) break; int row = filledCount / _columnCount + 1; int col = filledCount % _columnCount + 1; var pImage = GetAwardItemAt(row, col); if (pImage == null) break; var pItem = EC_IvtrItem.CreateItem(tid, 0, 1); if (pItem == null) continue; pItem.GetDetailDataFromLocal(); string strDesc = pItem.GetDesc(); pImage.SetHint(strDesc); pImage.SetOnClick(OnAwardItemClicked); var sprite = EC_IvtrItemUtils.Instance.ResolveItemIconSprite(pItem.m_tid); pImage.SetImage(sprite); var pEquip = (EC_IvtrEquip)EC_IvtrItem.CreateItem(pItem.m_tid, pItem.m_expire_date, pItem.m_iCount); pImage.SetColor( (pItem.IsEquipment() && pEquip.IsDestroying()) ? new Color32(128, 128, 128, 255) : new Color32(255, 255, 255, 255)); pImage.SetText(_AL("")); pImage.Show(true); filledCount++; } for (int idx = filledCount; idx < capacity; idx++) { int row = idx / _columnCount + 1; int col = idx % _columnCount + 1; var extra = GetAwardItemAt(row, col); if (extra == null) continue; extra.ClearCover(); extra.SetHint(_AL("")); extra.SetText(_AL("")); } int lastFilledRow = filledCount == 0 ? 0 : (filledCount - 1) / _columnCount + 1; ClearAwardGridForLocalEquipmentTest(lastFilledRow); } void ClearAwardGridForLocalEquipmentTest(int lastFilledRow) { for (int i = lastFilledRow + 1; i <= _rowCount; i++) { GetToggleBtnAt(i)?.gameObject.SetActive(false); for (int j = 1; j <= _columnCount; j++) { var pImage = GetAwardItemAt(i, j); if (pImage == null) break; pImage.ClearCover(); pImage.SetHint(_AL("")); pImage.SetText(_AL("")); pImage.Show(true); } } } void sprintf(out string szName, string format, int i) { // Convert C-style format specifiers to C# format // %02d -> zero-padded 2-digit integer (D2 format) // %d -> regular integer string csharpFormat = format .Replace("%02d", "{0:D2}") .Replace("%d", "{0}"); szName = string.Format(csharpFormat, i); } void sprintf(out string szName, string format, int i, int j) { // Convert C-style format specifiers to C# format for two parameters // %02d -> zero-padded 2-digit integer (D2 format) // %d -> regular integer // Replace occurrences sequentially: first -> {0}, second -> {1} string csharpFormat = format; int paramIndex = 0; csharpFormat = System.Text.RegularExpressions.Regex.Replace(csharpFormat, @"%02d|%d", match => { string replacement = paramIndex == 0 ? (match.Value == "%02d" ? "{0:D2}" : "{0}") : (match.Value == "%02d" ? "{1:D2}" : "{1}"); paramIndex++; return replacement; }); szName = string.Format(csharpFormat, i, j); } void a_sprintf(out string szText, string format, uint nNum) { // Convert C-style format specifiers to C# format // %d -> regular integer (for uint) string csharpFormat = format.Replace("%d", "{0}"); szText = string.Format(csharpFormat, nNum); } string _AL(string text) { // Localization helper - returns the string as-is // In C++ this would typically handle localization, but for now just return the text return text; } AwardItem GetAwardItemAt(int row, int column) { foreach (var item in _awardItems) { if (item.Position.x == row && item.Position.y == column) { return item; } } return null; } Toggle GetToggleBtnAt(int row) { int index = row - 1; if( index < 0 || index >= _radioButtons.Length) return null; return _radioButtons[index]; } void OnCommand_select(bool toggleValue) { if(toggleValue) m_pBtn_Confirm.gameObject.SetActive(true); } void OnCommand_confirm() { // int i; string szName; Toggle pRadio; uint idTask = GetData(); for( int i = 1; i <= _rowCount ; i++ ) { // sprintf(szName, "Rdo_Award%d", i); // pRadio = (PAUIRADIOBUTTON)GetDlgItem(szName); // if( !pRadio || !pRadio->IsShow() ) break; pRadio = GetToggleBtnAt(i); if(!pRadio || !pRadio.gameObject.activeInHierarchy) return; // if( pRadio.IsChecked() ) if( pRadio.isOn ) { if (m_bThroughNPC) { // GetGameSession().c2s_CmdNPCSevReturnTask(idTask, i - 1); UnityGameSession.c2s_CmdNPCSevReturnTask((int)idTask, i - 1); GetGameUIMan().EndNPCService(); } else if (m_bSpendContribution) { // TODO: Implement spend contribution logic // CECTaskInterface pTask = GetHostPlayer().GetTaskInterface(); // pTask.FinishTaskSpendingWorldContribution(idTask, i - 1); } else { // TODO: Implement choose award logic // CECTaskInterface pTask = GetHostPlayer().GetTaskInterface(); // pTask.ChooseAward(idTask, i - 1); } CloseDialogue(); break; } } itemInfoPanel.SetActive(false); } void OnAwardItemClicked(string hint, bool isOn, AwardItem awardItem) { if(isOn) { descriptionOutlet.Set(hint); PositionDetailPanelNearButton(awardItem); } else { descriptionOutlet.Set(""); } itemInfoPanel.SetActive(isOn); RefreshLayout(itemInfoPanel); } private void PositionDetailPanelNearButton(AwardItem awardItem) { if (itemInfoPanel == null || awardItem == null) { return; } var panelRect = itemInfoPanel.transform as RectTransform; if (panelRect == null) { return; } var buttonRect = awardItem.transform as RectTransform; if (buttonRect == null) { return; } var canvas = panelRect.GetComponentInParent(); if (canvas == null) { return; } var parentRect = panelRect.parent as RectTransform; if (parentRect == null) { return; } Camera eventCamera = canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera; Vector3 worldCenter = buttonRect.TransformPoint(buttonRect.rect.center); Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(eventCamera, worldCenter); Vector2 localPoint; if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(parentRect, screenPoint, eventCamera, out localPoint)) { return; } float btnHalfW = buttonRect.rect.width * 0.5f; float panelW = panelRect.rect.width; float panelH = panelRect.rect.height; float pivotX = panelRect.pivot.x; float pivotY = panelRect.pivot.y; // Compute right-placement candidate (panel's left edge at button's right edge + offset) // 计算右侧放置候选位置(面板左边缘在按钮右边缘 + 偏移量) float leftEdgeRightPlacement = localPoint.x + btnHalfW + itemInfoPanelOffset.x; float candidateXRight = leftEdgeRightPlacement + pivotX * panelW; // Compute left-placement candidate (panel's right edge at button's left edge - offset) // 计算左侧放置候选位置(面板右边缘在按钮左边缘 - 偏移量) float rightEdgeLeftPlacement = localPoint.x - btnHalfW - itemInfoPanelOffset.x; float candidateXLeft = rightEdgeLeftPlacement - (1f - pivotX) * panelW; // Vertical clamping honoring pivot // 垂直方向根据pivot进行限制 float minY = parentRect.rect.yMin + pivotY * panelH; float maxY = parentRect.rect.yMax - (1f - pivotY) * panelH; float candidateY = Mathf.Clamp(localPoint.y + itemInfoPanelOffset.y, minY, maxY); // Choose side based on available space // 根据可用空间选择放置侧 float rightEdgeOfRight = candidateXRight + (1f - pivotX) * panelW; float canvasRight = parentRect.rect.xMax; float canvasLeft = parentRect.rect.xMin; float leftEdgeOfLeft = candidateXLeft - pivotX * panelW; Vector2 finalPos; if (rightEdgeOfRight <= canvasRight) { finalPos = new Vector2(candidateXRight, candidateY); } else if (leftEdgeOfLeft >= canvasLeft) { finalPos = new Vector2(candidateXLeft, candidateY); } else { // Fallback: clamp within canvas horizontally // 回退:在画布内水平方向限制 float minX = canvasLeft + pivotX * panelW; float maxX = canvasRight - (1f - pivotX) * panelW; finalPos = new Vector2(Mathf.Clamp(candidateXRight, minX, maxX), candidateY); } panelRect.anchoredPosition = finalPos; } public void RefreshLayout(GameObject gameObject) { var parent = gameObject.GetComponent(); // Force Unity to rebuild layout immediately parent.ForceUpdateRectTransforms(); LayoutRebuilder.ForceRebuildLayoutImmediate(parent); } } }