Files
2026-04-21 14:57:43 +07:00

624 lines
20 KiB
C#

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<AwardItem>();
_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);
}
}
}
/// <summary>
/// Debug: equipment slot template ids only; rebuild with <see cref="EC_IvtrItem.CreateItem"/> +
/// <see cref="EC_IvtrItem.GetDetailDataFromLocal"/> for local prop/description testing.
/// </summary>
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<Canvas>();
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<RectTransform>();
// Force Unity to rebuild layout immediately
parent.ForceUpdateRectTransforms();
LayoutRebuilder.ForceRebuildLayoutImmediate(parent);
}
}
}