diff --git a/Assets/PerfectWorld/Scripts/GameData/GShopLoader.cs b/Assets/PerfectWorld/Scripts/GameData/GShopLoader.cs
index 00c3795227..90f21734fa 100644
--- a/Assets/PerfectWorld/Scripts/GameData/GShopLoader.cs
+++ b/Assets/PerfectWorld/Scripts/GameData/GShopLoader.cs
@@ -17,6 +17,12 @@ public class GShopLoader : MonoBehaviour
[Header("Loaded Data")]
public GShopData primaryShop = new GShopData();
public GShopData secondaryShop = new GShopData();
+
+ /// True after gshop.txt has been loaded and parsed successfully.
+ public bool IsPrimaryShopLoaded { get; private set; }
+
+ /// Invoked once when data is ready.
+ public event Action OnPrimaryShopLoaded;
async void Start()
{
@@ -37,6 +43,8 @@ public class GShopLoader : MonoBehaviour
if (await LoadShopData(GSHOP_ADDRESS, primaryShop))
{
Debug.Log($"Primary shop loaded: {primaryShop.items.Count} items, {primaryShop.mainTypes.Count} categories");
+ IsPrimaryShopLoaded = true;
+ OnPrimaryShopLoaded?.Invoke();
//LogShopData("Primary Shop", primaryShop);
}
diff --git a/Assets/PerfectWorld/Scripts/Task/UI/DlgTask.cs b/Assets/PerfectWorld/Scripts/Task/UI/DlgTask.cs
index 73eca8f317..6b78431e80 100644
--- a/Assets/PerfectWorld/Scripts/Task/UI/DlgTask.cs
+++ b/Assets/PerfectWorld/Scripts/Task/UI/DlgTask.cs
@@ -1036,9 +1036,9 @@ namespace BrewMonster.Scripts.Task.UI
// Only update description and name if task changed
if( idTask != m_idLastTask )
{
- _nameTaskText.SetText(EC_Utility.FormatForTextMeshPro(GetTaskNameWithColor(pTemp)));
+ _nameTaskText.SetText(EC_Utility.FormatForTextMeshPro(CECUIHelper.FormatCoordText(GetTaskNameWithColor(pTemp))));
//pTextDesc->SetText(FormatTaskText(pTemp->GetDescription(), pTextDesc->GetColor()));
- pTextDesc.SetText(EC_Utility.FormatForTextMeshPro(pTemp.GetDescription()));
+ pTextDesc.SetText(EC_Utility.FormatForTextMeshPro(CECUIHelper.FormatCoordText(pTemp.GetDescription())));
m_idLastTask = idTask;
bLastTaskChanged = true;
}
@@ -1154,9 +1154,9 @@ namespace BrewMonster.Scripts.Task.UI
// Update description when the selected task changes
if (idTask != m_idLastTask)
{
- _nameTaskText.SetText(EC_Utility.FormatForTextMeshPro(GetTaskNameWithColor(pTemp)));
+ _nameTaskText.SetText(EC_Utility.FormatForTextMeshPro(CECUIHelper.FormatCoordText(GetTaskNameWithColor(pTemp))));
Debug.Log("[DlgTask] SearchForTask name task: " + _nameTaskText.text);
- if (pTextDesc != null) pTextDesc.SetText(EC_Utility.FormatForTextMeshPro(pTemp.GetDescription()));
+ if (pTextDesc != null) pTextDesc.SetText(EC_Utility.FormatForTextMeshPro(CECUIHelper.FormatCoordText(pTemp.GetDescription())));
m_idLastTask = idTask;
bLastTaskChanged = true;
}
@@ -1619,7 +1619,7 @@ namespace BrewMonster.Scripts.Task.UI
// UnityEngine.UI.ScrollRect scrollRect = pTextItem.GetComponentInParent();
// float oldNormPos = scrollRect != null ? scrollRect.verticalNormalizedPosition : 0f;
- string formatted = EC_Utility.FormatForTextMeshPro(strNewTextItem ?? string.Empty);
+ string formatted = EC_Utility.FormatForTextMeshPro(CECUIHelper.FormatCoordText(strNewTextItem ?? string.Empty));
if (!string.Equals(formatted, pTextItem.text))
{
pTextItem.text = formatted;
diff --git a/Assets/PerfectWorld/Scripts/Task/UI/DlgTaskTrace.cs b/Assets/PerfectWorld/Scripts/Task/UI/DlgTaskTrace.cs
index 0bb4ae9e0f..ab58a21062 100644
--- a/Assets/PerfectWorld/Scripts/Task/UI/DlgTaskTrace.cs
+++ b/Assets/PerfectWorld/Scripts/Task/UI/DlgTaskTrace.cs
@@ -602,7 +602,7 @@ namespace BrewMonster.Scripts.Task.UI
bool bCanContributionFinish = !hasContributionAward;
// ACString strTask = pTempl->GetName();
- string strTask = pTempl.GetDescription();
+ string strTask = CECUIHelper.FormatCoordText(pTempl.GetDescription());
// ������ʾһ����������
if (topLevel)
{
diff --git a/Assets/PerfectWorld/Scripts/UI/EC_UIHelper.cs b/Assets/PerfectWorld/Scripts/UI/EC_UIHelper.cs
index c15df4ea73..86bce86e24 100644
--- a/Assets/PerfectWorld/Scripts/UI/EC_UIHelper.cs
+++ b/Assets/PerfectWorld/Scripts/UI/EC_UIHelper.cs
@@ -1,7 +1,10 @@
using System;
using System.Collections.Generic;
+using System.Text;
using BrewMonster.Network;
using BrewMonster.Managers;
+using ModelRenderer.Scripts.Common;
+using ModelRenderer.Scripts.GameData;
using BrewMonster.Scripts.Task;
using BrewMonster.Scripts;
using BrewMonster.Scripts.Chat;
@@ -102,7 +105,7 @@ namespace BrewMonster.Scripts.UI
// Fallback to coord_data.txt (C++: Configs/Coord_data.txt via CECGame::GetObjectCoord)
// 回退到 coord_data.txt(C++:Configs/Coord_data.txt,通过 CECGame::GetObjectCoord)
- if (BrewMonster.Network.EC_Game.TryGetFirstObjectCoord(id.ToString(), out var coordPos, out var mapName))
+ if (global::BrewMonster.Network.EC_Game.TryGetFirstObjectCoord(id.ToString(), out var coordPos, out var mapName))
{
in_table = true;
BMLogger.Log(
@@ -372,6 +375,96 @@ namespace BrewMonster.Scripts.UI
EC_ManMessage.PostMessage(0, 0, 0, msg);
}
+ ///
+ /// PW client: CECUIHelper::FormatCoordText — replaces @essenceId@ (monster template) and
+ /// $essenceId$ (NPC or mine template) with display names; when coordinates exist, wraps the name in a TMP
+ /// coord link like the main quest UI.
+ ///
+ public static string FormatCoordText(string szText)
+ {
+ if (string.IsNullOrEmpty(szText))
+ return string.Empty;
+
+ var sb = new StringBuilder(szText.Length + 48);
+ int len = szText.Length;
+ int i = 0;
+ elementdataman edm = global::BrewMonster.ElementDataManProvider.GetElementDataMan();
+
+ while (i < len)
+ {
+ int segStart = i;
+ while (i < len && szText[i] != '@' && szText[i] != '$')
+ i++;
+ sb.Append(szText, segStart, i - segStart);
+ if (i >= len)
+ break;
+
+ char open = szText[i];
+ i++;
+ int idStart = i;
+ while (i < len && szText[i] != '@' && szText[i] != '$')
+ i++;
+ if (i >= len)
+ {
+ sb.Append(open);
+ sb.Append(szText, idStart, i - idStart);
+ break;
+ }
+
+ char flag = szText[i];
+ string keyword = szText.Substring(idStart, i - idStart);
+ i++;
+
+ if (!uint.TryParse(keyword, out uint essenceId))
+ {
+ sb.Append(open);
+ sb.Append(keyword);
+ sb.Append(flag);
+ continue;
+ }
+
+ AppendEssencePlaceholder(sb, edm, essenceId, flag);
+ }
+
+ return sb.ToString();
+ }
+
+ static void AppendEssencePlaceholder(StringBuilder sb, elementdataman edm, uint essenceId, char flag)
+ {
+ string strName = null;
+ if (edm != null)
+ {
+ DATA_TYPE dt = default;
+ object p = edm.get_data_ptr(essenceId, ID_SPACE.ID_SPACE_ESSENCE, ref dt);
+ if (flag == '$')
+ {
+ if (dt == DATA_TYPE.DT_NPC_ESSENCE && p is NPC_ESSENCE npc)
+ strName = npc.Name;
+ else if (dt == DATA_TYPE.DT_MINE_ESSENCE && p is MINE_ESSENCE mine)
+ strName = ByteToStringUtils.UshortArrayToUnicodeString(mine.name);
+ }
+ else if (flag == '@')
+ {
+ if (dt == DATA_TYPE.DT_MONSTER_ESSENCE && p is MONSTER_ESSENCE mon)
+ strName = mon.Name;
+ }
+ }
+
+ if (string.IsNullOrEmpty(strName))
+ {
+ sb.Append("^00FF00?????^FFFFFF");
+ return;
+ }
+
+ bool inTable = false;
+ GetTaskObjectCoordinates((int)essenceId, ref inTable);
+ if (inTable)
+ sb.Append("").Append(strName)
+ .Append("");
+ else
+ sb.Append(strName);
+ }
+
public static string PolicySpecialCharReplace(
string szText,
CHAT_S2C.PolicyChatParameter pPolicyChatPara)
diff --git a/Assets/PerfectWorld/Scripts/UI/ShopUIManager.cs b/Assets/PerfectWorld/Scripts/UI/ShopUIManager.cs
index 76dac792ff..19baddf32f 100644
--- a/Assets/PerfectWorld/Scripts/UI/ShopUIManager.cs
+++ b/Assets/PerfectWorld/Scripts/UI/ShopUIManager.cs
@@ -52,11 +52,37 @@ public class ShopUIManager : MonoBehaviour
//-1 means all sub types
public SubTypeShop subTypeShop;
private int currentSubType = -1;
+ private bool _shopLoaderEventsSubscribed;
+
void Start()
{
InitializeUI();
SetupEventListeners();
InitializePool();
+ SubscribeShopLoaderEvents();
+ }
+
+ void SubscribeShopLoaderEvents()
+ {
+ if (_shopLoaderEventsSubscribed)
+ return;
+ if (shopLoader == null)
+ shopLoader = FindFirstObjectByType();
+ if (shopLoader == null)
+ return;
+
+ shopLoader.OnPrimaryShopLoaded += OnPrimaryShopDataReady;
+ _shopLoaderEventsSubscribed = true;
+
+ // Load may have finished before we subscribed (e.g. script order).
+ if (shopLoader.IsPrimaryShopLoaded)
+ OnPrimaryShopDataReady();
+ }
+
+ void OnPrimaryShopDataReady()
+ {
+ if (shopMainPanel != null && shopMainPanel.activeSelf)
+ RefreshShopDisplay();
}
void InitializePool()
@@ -106,8 +132,10 @@ public class ShopUIManager : MonoBehaviour
{
if (shopMainPanel != null)
{
- OnCategorySelected(0);
- RefreshShopDisplay();
+ SubscribeShopLoaderEvents();
+ shopMainPanel.SetActive(true);
+ // Always apply category 0 when opening; OnCategorySelected(0) no-ops if currentCategory is already 0.
+ OnCategorySelected(0, forceRefresh: true);
ApplyPendingCash();
UnityGameSession.RequesrQueryPlayerCash();
}
@@ -175,9 +203,9 @@ public class ShopUIManager : MonoBehaviour
shopDetailPanel.SetActive(false);
}
- void OnCategorySelected(int categoryIndex)
+ void OnCategorySelected(int categoryIndex, bool forceRefresh = false)
{
- if (categoryIndex == currentCategory)
+ if (!forceRefresh && categoryIndex == currentCategory)
return;
float startTime = Time.realtimeSinceStartup;
@@ -572,6 +600,9 @@ public class ShopUIManager : MonoBehaviour
void OnDestroy()
{
+ if (shopLoader != null && _shopLoaderEventsSubscribed)
+ shopLoader.OnPrimaryShopLoaded -= OnPrimaryShopDataReady;
+
// Clean up pooled objects
if (useObjectPooling && itemPanelPool != null)
{