From 2e6989c604bd18c8b5974bc17b25bff366045ac2 Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Mon, 13 Apr 2026 11:00:15 +0700 Subject: [PATCH 1/2] Fix PW-137 mall not opening correct --- .../Scripts/GameData/GShopLoader.cs | 8 ++++ .../PerfectWorld/Scripts/UI/ShopUIManager.cs | 39 +++++++++++++++++-- 2 files changed, 43 insertions(+), 4 deletions(-) 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/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) { From 05410d236dbaa46839f59d5202afeb7316daf946 Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Mon, 13 Apr 2026 12:31:49 +0700 Subject: [PATCH 2/2] Fix text PW-138 --- .../PerfectWorld/Scripts/Task/UI/DlgTask.cs | 10 +- .../Scripts/Task/UI/DlgTaskTrace.cs | 2 +- Assets/PerfectWorld/Scripts/UI/EC_UIHelper.cs | 95 ++++++++++++++++++- 3 files changed, 100 insertions(+), 7 deletions(-) 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)