fix some UI

This commit is contained in:
VuNgocHaiC7
2026-04-07 17:00:48 +07:00
parent 48d1171689
commit a609ebda1c
7 changed files with 7733 additions and 23730 deletions
@@ -2,12 +2,13 @@
// Creator : Converted from C++ EC_Shop
// Date : 2024
using BrewMonster.Network;
using BrewMonster.Scripts;
using CSNetwork.C2SCommand;
using System.Text.RegularExpressions;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using BrewMonster.Scripts;
using BrewMonster.Network;
using CSNetwork.C2SCommand;
public class NPCShopDetailPanel : MonoBehaviour
{
@@ -47,7 +48,7 @@ public class NPCShopDetailPanel : MonoBehaviour
UpdateDisplay();
}
void UpdateDisplay()
public void UpdateDisplay()
{
if (currentItem.id == 0)
{
@@ -59,6 +60,9 @@ public class NPCShopDetailPanel : MonoBehaviour
if (itemDescriptionText != null)
{
string description = GetItemDescription((int)currentItem.id);
Debug.Log("[NPCShopDetail] RAW:\n" + description);
description = SanitizeDescription(description);
Debug.Log("[NPCShopDetail] Sanitized:\n" + description);
itemDescriptionText.Set(description);
}
@@ -85,7 +89,57 @@ public class NPCShopDetailPanel : MonoBehaviour
LoadItemIcon(itemIconImage, (int)currentItem.id);
}
}
private string SanitizeDescription(string desc)
{
if (string.IsNullOrWhiteSpace(desc))
return string.Empty;
// Normalize escaped + real line breaks (including unicode line separators)
desc = Regex.Replace(
desc,
@"\\r\\n|\\n|\\r|\r\n|\r|\u2028|\u2029",
"\n",
RegexOptions.CultureInvariant);
desc = Regex.Replace(
desc,
@"\^[0-9A-Fa-f]{6}",
string.Empty,
RegexOptions.CultureInvariant);
string[] lines = desc.Split('\n');
var sb = new System.Text.StringBuilder();
for (int i = 0; i < lines.Length; i++)
{
string line = lines[i];
if (string.IsNullOrWhiteSpace(line))
continue;
line = Regex.Replace(line, @"[\u200B-\u200D\uFEFF]", string.Empty, RegexOptions.CultureInvariant);
string plain = Regex.Replace(line, @"<[^>]*>", string.Empty, RegexOptions.CultureInvariant)
.Replace('', ':')
.Trim();
if (Regex.IsMatch(plain, @"^(?:ID|Price)\s*:", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
continue;
line = Regex.Replace(
line,
@"(?i)\b(?:ID|Price)\b\s*[:]\s*\d+\b",
string.Empty,
RegexOptions.CultureInvariant).Trim();
if (string.IsNullOrWhiteSpace(line))
continue;
if (sb.Length > 0)
sb.AppendLine();
sb.Append(line);
}
return sb.ToString().Trim();
}
void LoadItemIcon(Image iconImage, int itemId)
{
if (itemId <= 0 || iconImage == null)
@@ -161,6 +215,12 @@ public class NPCShopDetailPanel : MonoBehaviour
return "No description available.";
}
public void ClearText()
{
if(itemDescriptionText != null)
itemDescriptionText.Set(string.Empty);
}
void OnCloseClicked()
{
@@ -223,6 +283,7 @@ public class NPCShopDetailPanel : MonoBehaviour
{
[SerializeField] public Text legacy;
[SerializeField] public TMPro.TextMeshProUGUI tmp;
Color myColorDecimal = new Color(1f, 0.816f, 0.365f, 1f);
public void Set(string value)
{
@@ -233,6 +294,7 @@ public class NPCShopDetailPanel : MonoBehaviour
if (tmp != null)
{
tmp.text = EC_Utility.FormatForTextMeshPro(value ?? string.Empty);
Debug.Log($"[TextOutlet] Set text: {tmp.text}");
}
}
}
@@ -26,13 +26,16 @@ public class NPCShopUIManager : AUIDialog
public GameObject contentRight;
public GameObject contentMidSell;
public GameObject item_info;
public ScrollRect scrollDetailItem;
public ScrollRect scrollListItem;
[Header("Texts")]
public TextMeshProUGUI itemDetailNameText;
public TextMeshProUGUI itemMoneyText;
public TextOutlet itemDescriptionText;
public NPCShopDetailPanel itemDescriptionText;
public TextMeshProUGUI itemsBuyAmountText;
public TextMeshProUGUI itemsBuyTotalMoneyText;
public TextMeshProUGUI itemsSellTotalMoneyText;
[Header("Tabs")]
public Transform tabButtonContainer;
@@ -82,7 +85,6 @@ public class NPCShopUIManager : AUIDialog
/// <summary>Current NPC id for this shop session. Send SEVNPC_HELLO with this before buy.</summary>
public uint CurrentNPCID => currentNPCID;
private NPCShopDetailPanel detailPanelScript;
public override void OnEnable()
{
@@ -392,45 +394,47 @@ public class NPCShopUIManager : AUIDialog
// Initialize buy array with at least one option
shopItem.buy = new GShopBuyOption[4]; // GShopItem supports up to 4 buy options
// Resolve display name/price in a type-agnostic way.
// Some element types come back as DATA_TYPE values we don't special-case here (arrows, flyswords, fashion, etc).
string itemName = EC_IvtrItemUtils.Instance.ResolveItemName(unchecked((int)good.id));
if (string.IsNullOrWhiteSpace(itemName))
itemName = $"Item_{good.id}";
static int ExtractInt(object data, params string[] fieldOrPropNames)
// Get item name and price based on type
string itemName = "Unknown";
int shopPrice = 0;
switch (itemDataType)
{
if (data == null || fieldOrPropNames == null || fieldOrPropNames.Length == 0)
return 0;
var t = data.GetType();
for (int i = 0; i < fieldOrPropNames.Length; i++)
{
string name = fieldOrPropNames[i];
if (string.IsNullOrEmpty(name))
continue;
var field = t.GetField(name, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.IgnoreCase);
if (field != null && field.FieldType == typeof(int))
{
try { return (int)field.GetValue(data); } catch { }
}
var prop = t.GetProperty(name, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.IgnoreCase);
if (prop != null && prop.PropertyType == typeof(int) && prop.CanRead)
{
try { return (int)prop.GetValue(data); } catch { }
}
}
return 0;
case DATA_TYPE.DT_WEAPON_ESSENCE:
var weaponEssence = (WEAPON_ESSENCE)itemData;
itemName = weaponEssence.Name;
shopPrice = weaponEssence.shop_price;
break;
case DATA_TYPE.DT_ARMOR_ESSENCE:
var armorEssence = (ARMOR_ESSENCE)itemData;
itemName = armorEssence.Name;
shopPrice = armorEssence.shop_price;
break;
case DATA_TYPE.DT_MEDICINE_ESSENCE:
var medicineEssence = (MEDICINE_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(medicineEssence.name);
shopPrice = medicineEssence.shop_price;
break;
case DATA_TYPE.DT_DECORATION_ESSENCE:
var decorationEssence = (DECORATION_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(decorationEssence.name);
shopPrice = decorationEssence.shop_price;
break;
case DATA_TYPE.DT_STONE_ESSENCE:
var stoneEssence = (STONE_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(stoneEssence.name);
shopPrice = stoneEssence.shop_price;
break;
case DATA_TYPE.DT_MATERIAL_ESSENCE:
var materialEssence = (MATERIAL_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(materialEssence.name);
shopPrice = materialEssence.shop_price;
break;
default:
itemName = $"Item_{good.id}";
break;
}
// Prefer shop_price; fall back to base price if that's all we have.
int shopPrice = ExtractInt(itemData, "shop_price");
if (shopPrice <= 0)
shopPrice = ExtractInt(itemData, "price");
shopItem.name = itemName;
// Set price from contribution cost or shop price
@@ -458,7 +462,42 @@ public class NPCShopUIManager : AUIDialog
{
if (itemPanelPrefab == null || itemContainer == null)
return;
if(scrollListItem != null)
{
scrollListItem.verticalNormalizedPosition = 1f;
}
if (scrollDetailItem != null)
{
scrollDetailItem.verticalNormalizedPosition = 1f;
}
if (itemDetailNameText != null)
{
itemDetailNameText.text = string.Empty;
}
if (itemMoneyText != null)
{
itemMoneyText.text = string.Empty;
}
if (itemDescriptionText != null)
{
itemDescriptionText.ClearText();
}
if (itemIconImage != null)
{
itemIconImage.sprite = khung_item;
}
if (itemsBuyTotalMoneyText != null)
{
itemsBuyTotalMoneyText.text = string.Empty;
}
GameObject itemPanel = Instantiate(itemPanelPrefab, itemContainer);
itemPanel.SetActive(true);
@@ -494,27 +533,37 @@ public class NPCShopUIManager : AUIDialog
return;
SetupDetailPanel(item, this, shopItemIndex);
// Ensure detail panel script is available
if (detailPanelScript == null)
{
}
if (detailPanelScript != null)
{
}
else
{
Debug.LogError("[NPCShopUIManager] NPCShopDetailPanel component not found on npcShopDetailPanel GameObject!");
}
}
public override void CloseDialogue()
{
base.CloseDialogue();
if (itemDetailNameText != null)
{
itemDetailNameText.text = string.Empty;
}
if (itemMoneyText != null)
{
itemMoneyText.text = string.Empty;
}
if (itemDescriptionText != null)
{
itemDescriptionText.ClearText();
}
if (itemIconImage != null)
{
itemIconImage.sprite = khung_item;
}
if(itemsBuyTotalMoneyText != null)
{
itemsBuyTotalMoneyText.text = string.Empty;
}
EC_Game.GetGameRun().GetUIManager().GetInGameUIMan().EndNPCService();
}
@@ -590,14 +639,18 @@ public class NPCShopUIManager : AUIDialog
itemMoneyText.text = BuyUiEmptyValue;
}
if (scrollDetailItem != null)
{
scrollDetailItem.verticalNormalizedPosition = 1f;
}
buyCount = BuyCountMin;
UpdateBuyPriceTexts();
// Set item description
if (itemDescriptionText != null)
{
string description = GetItemDescription((int)currentItem.id);
itemDescriptionText.Set(description);
itemDescriptionText.SetupDetailPanel(currentItem, this, shopItemIndex);
}
// if (itemPriceText != null)
@@ -708,7 +761,26 @@ public class NPCShopUIManager : AUIDialog
if (itemsBuyTotalMoneyText != null)
{
itemsBuyTotalMoneyText.text = total.ToString();
uint playerMoney = 0;
try
{
var host = CECGameRun.Instance?.GetHostPlayer();
if (host != null)
{
playerMoney = host.GetMoneyAmount();
}
}
catch
{
playerMoney = 0;
}
string totalText = total.ToString();
string playerMoneyStr = playerMoney.ToString();
itemsBuyTotalMoneyText.text = total > playerMoney
? $"<color=red>{total}</color>/{playerMoney}"
: $"{total}/{playerMoney}";
}
if (m_btn_buy != null)
@@ -1138,28 +1210,6 @@ public class NPCShopUIManager : AUIDialog
m_use_Item.onClick.RemoveListener(OnUseItemClicked);
}
// === Text Outlet Helper ===
[System.Serializable]
public class TextOutlet
{
[SerializeField] public Text legacy;
[SerializeField] public TMPro.TextMeshProUGUI tmp;
public void Set(string value)
{
if (legacy != null)
{
legacy.text = EC_Utility.FormatForLegacyText(value ?? string.Empty);
}
if (tmp != null)
{
string formatTextDeail = EC_Utility.FormatForTextMeshPro(value ?? string.Empty);
formatTextDeail.Replace("<color=#FFFFFF>", "<color=#FFD05D>");
tmp.text = formatTextDeail;
}
}
}
private void SetupSellReadySlotListeners()
{
if (itemReadySell == null)
@@ -1237,7 +1287,75 @@ public class NPCShopUIManager : AUIDialog
private void UpdateSellTotalPriceText()
{
// Sell total text removed from this manager.
return;
long totalSellValue = 0;
int stageCount = 0;
try
{
var host = CECGameRun.Instance?.GetHostPlayer();
var inv = host?.GetInventory(0);
if (inv == null || sellSlotToSourceSlot == null)
{
if (itemsSellTotalMoneyText != null)
itemsSellTotalMoneyText.text = BuyUiEmptyValue;
if (m_btn_sell != null)
m_btn_sell.interactable = false;
return;
}
foreach (var pair in sellSlotToSourceSlot)
{
int sourceSlot = pair.Value;
if (sourceSlot < 0 || sourceSlot >= inv.GetSize())
continue;
var item = inv.GetItem(sourceSlot, false);
if (item == null || item.m_iCount <= 0)
continue;
if (!item.IsSellable())
continue;
try
{
item.GetDetailDataFromLocal();
}
catch
{
}
int unitPrice = item.GetScaledPrice();
long itemTotal = (long)unitPrice * item.m_iCount;
if (itemTotal > 0)
{
totalSellValue += itemTotal;
stageCount++;
}
}
}
catch(Exception ex)
{
}
if (itemsSellTotalMoneyText != null)
{
if(stageCount == 0 || totalSellValue <= 0)
{
itemsSellTotalMoneyText.text = BuyUiEmptyValue;
}
else
{
itemsSellTotalMoneyText.text = totalSellValue.ToString();
}
}
if (m_btn_sell != null)
{
m_btn_sell.interactable = stageCount > 0 && totalSellValue > 0;
}
}
}
+47 -7
View File
@@ -1840,11 +1840,11 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 777847736648841921, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_fontSize
value: 48.2
value: 36
objectReference: {fileID: 0}
- target: {fileID: 777847736648841921, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_fontSizeBase
value: 48.2
value: 36
objectReference: {fileID: 0}
- target: {fileID: 777847736648841921, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_enableAutoSizing
@@ -1934,25 +1934,49 @@ PrefabInstance:
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1900527214026617767, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMax.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1900527214026617767, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMin.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1900527214026617767, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_SizeDelta.x
value: 200
value: 171
objectReference: {fileID: 0}
- target: {fileID: 1900527214026617767, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_SizeDelta.y
value: 67.9
value: 56.91
objectReference: {fileID: 0}
- target: {fileID: 1900527214026617767, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.x
value: 532
value: 85.5
objectReference: {fileID: 0}
- target: {fileID: 1900527214026617767, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.y
value: -37
value: -28.455
objectReference: {fileID: 0}
- target: {fileID: 4313766541946487796, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMax.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 4313766541946487796, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMin.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 4313766541946487796, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.x
value: 85.5
objectReference: {fileID: 0}
- target: {fileID: 4313766541946487796, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.y
value: -152.91
objectReference: {fileID: 0}
- target: {fileID: 6102967088919909530, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.y
value: 10.8
value: 8
objectReference: {fileID: 0}
- target: {fileID: 6830833846243993097, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_Name
@@ -1962,6 +1986,22 @@ PrefabInstance:
propertyPath: m_IsActive
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6900679650323527362, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMax.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6900679650323527362, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMin.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6900679650323527362, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.x
value: 85.5
objectReference: {fileID: 0}
- target: {fileID: 6900679650323527362, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.y
value: -88.91
objectReference: {fileID: 0}
- target: {fileID: 7209086543831860202, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMax.y
value: 1
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+47 -7
View File
@@ -1980,11 +1980,11 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 777847736648841921, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_fontSize
value: 48.2
value: 36
objectReference: {fileID: 0}
- target: {fileID: 777847736648841921, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_fontSizeBase
value: 48.2
value: 36
objectReference: {fileID: 0}
- target: {fileID: 777847736648841921, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_enableAutoSizing
@@ -2074,25 +2074,49 @@ PrefabInstance:
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1900527214026617767, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMax.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1900527214026617767, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMin.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1900527214026617767, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_SizeDelta.x
value: 200
value: 171
objectReference: {fileID: 0}
- target: {fileID: 1900527214026617767, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_SizeDelta.y
value: 67.9
value: 55.9082
objectReference: {fileID: 0}
- target: {fileID: 1900527214026617767, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.x
value: 532
value: 85.5
objectReference: {fileID: 0}
- target: {fileID: 1900527214026617767, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.y
value: -37
value: -27.9541
objectReference: {fileID: 0}
- target: {fileID: 4313766541946487796, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMax.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 4313766541946487796, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMin.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 4313766541946487796, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.x
value: 85.5
objectReference: {fileID: 0}
- target: {fileID: 4313766541946487796, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.y
value: -151.9082
objectReference: {fileID: 0}
- target: {fileID: 6102967088919909530, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.y
value: 10.8
value: 3.5
objectReference: {fileID: 0}
- target: {fileID: 6830833846243993097, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_Name
@@ -2102,6 +2126,22 @@ PrefabInstance:
propertyPath: m_IsActive
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6900679650323527362, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMax.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6900679650323527362, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMin.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6900679650323527362, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.x
value: 85.5
objectReference: {fileID: 0}
- target: {fileID: 6900679650323527362, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.y
value: -87.9082
objectReference: {fileID: 0}
- target: {fileID: 7209086543831860202, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMax.y
value: 1