From d8d7502fe09129335455c2c037eb6b34ec5d0d86 Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Wed, 3 Dec 2025 16:50:59 +0700 Subject: [PATCH] Integrate and asign prefab --- .../UI/DialogScriptTableObject.asset | 2 + .../Network/CSNetwork/Common/ExpTypes.cs | 139 +++++++++--------- .../Scripts/Pattern/CECObserver.cs | 139 +++++++++++++++++- .../PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs | 53 +++++++ .../Scripts/UI/GamePlay/EC_GameUIMan.cs | 10 ++ Assets/Scripts/CECUIManager.cs | 8 + 6 files changed, 278 insertions(+), 73 deletions(-) diff --git a/Assets/PerfectWorld/Resources/UI/DialogScriptTableObject.asset b/Assets/PerfectWorld/Resources/UI/DialogScriptTableObject.asset index a9fbcb99ec..24be82aded 100644 --- a/Assets/PerfectWorld/Resources/UI/DialogScriptTableObject.asset +++ b/Assets/PerfectWorld/Resources/UI/DialogScriptTableObject.asset @@ -15,3 +15,5 @@ MonoBehaviour: lstPrefabDialog: - id: DialogNPC prefab: {fileID: 8237288432181259026, guid: 7653e7e64393ec24c903f0606499b8c4, type: 3} + - id: DialogNPCShop + prefab: {fileID: 8237288432181259026, guid: eaeb778b6aab3d74299373b3a96b72c4, type: 3} diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/Common/ExpTypes.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/Common/ExpTypes.cs index f9487dfb33..7312c371d3 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/Common/ExpTypes.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/Common/ExpTypes.cs @@ -5,7 +5,7 @@ using System.Text; namespace CSNetwork.Common { - internal class ExpTypes + public class ExpTypes { // ts [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)] @@ -110,73 +110,74 @@ namespace CSNetwork.Common public int life; } } - - public enum SERVICE_TYPE : int - { - // ½»Ì¸·þÎñ - NPC_TALK = int.MinValue, - // ³öÊÛÉÌÆ·µÄ·þÎñ - NPC_SELL, - // ÊÕ¹ºÉÌÆ·µÄ·þÎñ - NPC_BUY, - // ÐÞÀíÉÌÆ·µÄ·þÎñ - NPC_REPAIR, - // ÏâǶ·þÎñ - NPC_INSTALL, - // ²ð³ý·þÎñ - NPC_UNINSTALL, - // ÈÎÎñÏà¹Ø·þÎñ,·Ö·¢ÈÎÎñºÍÍê³ÉÈÎÎñÒÔ¼°·¢·ÅÈÎÎñÎïÆ· - NPC_GIVE_TASK, - NPC_COMPLETE_TASK, - NPC_GIVE_TASK_MATTER, - // ½ÌÊÚÏà¹Ø·þÎñ - NPC_SKILL, - // ÖÎÁÆ·þÎñ - NPC_HEAL, - // ´«ËÍ·þÎñ - NPC_TRANSMIT, - // ÔËÊä·þÎñ - NPC_TRANSPORT, - // ´úÊÛ·þÎñ - NPC_PROXY, - // ´æ´¢ÎïÆ·¡¢½ðÇ® - NPC_STORAGE, - // Éú²ú·þÎñ - NPC_MAKE, - // ·Ö½â·þÎñ - NPC_DECOMPOSE, - // TALK·µ»Ø - TALK_RETURN, - // ½áÊø¶Ô»° - TALK_EXIT, - // ²Ö¿âÃÜÂë - NPC_STORAGE_PASSWORD, - // ¼ø¶¨·þÎñ - NPC_IDENTIFY, - // ·ÅÆúÈÎÎñ - TALK_GIVEUP_TASK, - // ³ÇÕ½ÅÚËþ½¨Ôì·þÎñ - NPC_WAR_TOWERBUILD, - // Ï´µã·þÎñ - NPC_RESETPROP, - // ³èÎï¸ÄÃû·þÎñ - NPC_PETNAME, - // ³èÎïѧϰ¼¼ÄÜ·þÎñ - NPC_PETLEARNSKILL, - // ³èÎïÒÅÍü¼¼ÄÜ·þÎñ - NPC_PETFORGETSKILL, - // ×°±¸°ó¶¨·þÎñ - NPC_EQUIPBIND, - // ×°±¸Ïú»Ù·þÎñ - NPC_EQUIPDESTROY, - // ×°±¸½â³ýÏú»Ù·þÎñ - NPC_EQUIPUNDESTROY, - // ÕʺŲֿâ - NPC_ACCOUNT_STORAGE, - // ïÔ¿Ì·þÎñ - NPC_ENGRAVE, - // ×°±¸ÖØÖý£¨Ëæ»úÊôÐÔ£© - NPC_RANDPROP, - }; } + + public enum SERVICE_TYPE : int + { + // ½»Ì¸·þÎñ + NPC_TALK = int.MinValue, + // ³öÊÛÉÌÆ·µÄ·þÎñ + NPC_SELL, + // ÊÕ¹ºÉÌÆ·µÄ·þÎñ + NPC_BUY, + // ÐÞÀíÉÌÆ·µÄ·þÎñ + NPC_REPAIR, + // ÏâǶ·þÎñ + NPC_INSTALL, + // ²ð³ý·þÎñ + NPC_UNINSTALL, + // ÈÎÎñÏà¹Ø·þÎñ,·Ö·¢ÈÎÎñºÍÍê³ÉÈÎÎñÒÔ¼°·¢·ÅÈÎÎñÎïÆ· + NPC_GIVE_TASK, + NPC_COMPLETE_TASK, + NPC_GIVE_TASK_MATTER, + // ½ÌÊÚÏà¹Ø·þÎñ + NPC_SKILL, + // ÖÎÁÆ·þÎñ + NPC_HEAL, + // ´«ËÍ·þÎñ + NPC_TRANSMIT, + // ÔËÊä·þÎñ + NPC_TRANSPORT, + // ´úÊÛ·þÎñ + NPC_PROXY, + // ´æ´¢ÎïÆ·¡¢½ðÇ® + NPC_STORAGE, + // Éú²ú·þÎñ + NPC_MAKE, + // ·Ö½â·þÎñ + NPC_DECOMPOSE, + // TALK·µ»Ø + TALK_RETURN, + // ½áÊø¶Ô»° + TALK_EXIT, + // ²Ö¿âÃÜÂë + NPC_STORAGE_PASSWORD, + // ¼ø¶¨·þÎñ + NPC_IDENTIFY, + // ·ÅÆúÈÎÎñ + TALK_GIVEUP_TASK, + // ³ÇÕ½ÅÚËþ½¨Ôì·þÎñ + NPC_WAR_TOWERBUILD, + // Ï´µã·þÎñ + NPC_RESETPROP, + // ³èÎï¸ÄÃû·þÎñ + NPC_PETNAME, + // ³èÎïѧϰ¼¼ÄÜ·þÎñ + NPC_PETLEARNSKILL, + // ³èÎïÒÅÍü¼¼ÄÜ·þÎñ + NPC_PETFORGETSKILL, + // ×°±¸°ó¶¨·þÎñ + NPC_EQUIPBIND, + // ×°±¸Ïú»Ù·þÎñ + NPC_EQUIPDESTROY, + // ×°±¸½â³ýÏú»Ù·þÎñ + NPC_EQUIPUNDESTROY, + // ÕʺŲֿâ + NPC_ACCOUNT_STORAGE, + // ïÔ¿Ì·þÎñ + NPC_ENGRAVE, + // ×°±¸ÖØÖý£¨Ëæ»úÊôÐÔ£© + NPC_RANDPROP, + }; } + diff --git a/Assets/PerfectWorld/Scripts/Pattern/CECObserver.cs b/Assets/PerfectWorld/Scripts/Pattern/CECObserver.cs index d6879c5377..c118b7a182 100644 --- a/Assets/PerfectWorld/Scripts/Pattern/CECObserver.cs +++ b/Assets/PerfectWorld/Scripts/Pattern/CECObserver.cs @@ -1,18 +1,149 @@ +// File : CECObserver.cs +// Creator : Xu Wenbin +// Date : 2014/4/10 +// Converted to C#: 2024 + using System; +using System.Collections.Generic; +using System.Linq; using UnityEngine; namespace BrewMonster { - public class CECObservableChange : IDisposable + // class CECObservableChange + // 封装 CECObservable 的变化通知数据 / Encapsulates change notification data for CECObservable + public class CECObservableChange : IDisposable { public virtual void Dispose() { // Cleanup logic here } - }; + } - public class CECObserver + // class CECObserver + // 观察者基类:观察的 Model / Observer base class: observes Model + public class CECObserver { - + public virtual void OnRegistered(Model model) { } + public virtual void OnModelChange(Model model, CECObservableChange change) { } + public virtual void OnUnregister(Model model) { } + } + + // class CECObservable + // 可观察者基类:被观察的 Model / Observable base class: the Model being observed + // 提供观察者注册及通知功能 / Provides observer registration and notification functionality + public class CECObservable where Model : class + { + private struct ObserverImpl + { + public CECObserver observer; // 观察者指针 / Observer pointer + public int observeTimes; // 观察次数:为正数时,收到指定次数的 OnModelChange 消息后自动取消注册,为负数时为永久,为0值时无效 / Observe times: when positive, auto-unregister after receiving specified OnModelChange messages; when negative, permanent; when 0, invalid + public bool autoDeleteOnUnregister; // 取消注册时是否自动 delete / Whether to auto-delete on unregister + + public ObserverImpl(CECObserver observer, int observeTimes, bool autoDeleteOnUnregister) + { + this.observer = observer; + this.observeTimes = observeTimes; + this.autoDeleteOnUnregister = autoDeleteOnUnregister; + + if (this.observeTimes == 0) + { + Debug.Assert(false, "Observer times cannot be 0"); + this.observeTimes = 1; // 默认观察次数为1 / Default observe times to 1 + } + } + + public bool Equals(ObserverImpl rhs) + { + return this.observer == rhs.observer; + } + + public bool Equals(CECObserver pObserver) + { + return this.observer == pObserver; + } + + public bool WillNotObserve() + { + // 是否不再观察目标 / Whether no longer observing target + return observeTimes == 0; + } + + public bool ObserveOnce() + { + // 收到一次观察消息时调用,返回是否不再观察 / Called when receiving one observe message, returns whether no longer observing + if (observeTimes > 0) + { + --observeTimes; + } + return WillNotObserve(); + } + } + + private List m_observers = new List(); + + protected Model AsModel() + { + return this as Model; + } + + public bool IsObserverRegistered(CECObserver pObserver) + { + return m_observers.Any(impl => impl.Equals(pObserver)); + } + + public bool RegisterObserver(CECObserver pObserver, int observeTimes = -1, bool autoDeleteOnUnregister = false) + { + bool bRegistered = false; + if (!IsObserverRegistered(pObserver)) + { + m_observers.Add(new ObserverImpl(pObserver, observeTimes, autoDeleteOnUnregister)); + pObserver.OnRegistered(AsModel()); + bRegistered = true; + } + return bRegistered; + } + + public bool UnregisterObserver(CECObserver pObserver) + { + int index = m_observers.FindIndex(impl => impl.Equals(pObserver)); + if (index >= 0) + { + return UnregisterObserverImpl(index); + } + return false; + } + + public void NotifyObservers(CECObservableChange pChange) + { + Model pModel = AsModel(); + for (int i = m_observers.Count - 1; i >= 0; i--) + { + ObserverImpl observerImpl = m_observers[i]; + observerImpl.observer.OnModelChange(pModel, pChange); + if (observerImpl.ObserveOnce()) + { + UnregisterObserverImpl(i); + } + } + } + + private bool UnregisterObserverImpl(int index) + { + bool bUnRegistered = false; + if (index >= 0 && index < m_observers.Count) + { + ObserverImpl observerImpl = m_observers[index]; + observerImpl.observer.OnUnregister(AsModel()); + if (observerImpl.autoDeleteOnUnregister) + { + // In C#, we don't manually delete objects, but we can set to null + // The GC will handle cleanup + } + m_observers.RemoveAt(index); + bUnRegistered = true; + } + return bUnRegistered; + } } } diff --git a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs index f5f8dd72c1..ed1249f46c 100644 --- a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs +++ b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs @@ -1460,6 +1460,32 @@ namespace BrewMonster.UI //pButton = (Button)pShow2.GetDlgItem("Btn_QuestItem"); //pButton.SetPushed(false); //GetGameUIMan().m_pDlgInventory.SetShowItem(CDlgInventory::INVENTORY_ITEM_NORMAL); + if (pCurNPCEssence.HasValue) + { + uint npcID = pCurNPCEssence.Value.id; + NPCShopUIManager shopManager = FindFirstObjectByType(); + if (shopManager == null) + { + // Instantiate NPCShopUIManager if not found, similar to how DlgNPC is instantiated + CECGameUIMan gameUIMan = GetGameUIMan(); + DialogScriptTableObject dialogResource = gameUIMan.GetDialogResource(); + Canvas canvas = gameUIMan.GetCanvas(); + + if (dialogResource != null && canvas != null) + { + GameObject ob = dialogResource.GetPrefabDialog("DialogNPCShop"); + if (ob != null) + { + shopManager = GameObject.Instantiate(ob, canvas.transform).GetComponent(); + } + } + } + + if (shopManager != null) + { + shopManager.OpenNPCShop(npcID); + } + } } else if (idFunction == (int)SERVICE_TYPE.NPC_INSTALL) { @@ -3072,6 +3098,7 @@ namespace BrewMonster.UI SetData(NPC_DIALOG.NPC_DIALOG_TALK, ""); } } + //Show Mua item id = 0 else { object pData1 = m_pLst_Main.GetItemDataPtr(nCurSel, 0, ""); @@ -3111,6 +3138,32 @@ namespace BrewMonster.UI //pButton = (PAUISTILLIMAGEBUTTON)pShow2.GetDlgItem("Btn_QuestItem"); //pButton.SetPushed(false); //GetGameUIMan().m_pDlgInventory.SetShowItem(CDlgInventory::INVENTORY_ITEM_NORMAL); + if (pCurNPCEssence.HasValue) + { + uint npcID = pCurNPCEssence.Value.id; + NPCShopUIManager shopManager = FindFirstObjectByType(); + if (shopManager == null) + { + // Instantiate NPCShopUIManager if not found, similar to how DlgNPC is instantiated + CECGameUIMan gameUIMan = GetGameUIMan(); + DialogScriptTableObject dialogResource = gameUIMan.GetDialogResource(); + Canvas canvas = gameUIMan.GetCanvas(); + + if (dialogResource != null && canvas != null) + { + GameObject ob = dialogResource.GetPrefabDialog("DialogNPCShop"); + if (ob != null) + { + shopManager = GameObject.Instantiate(ob, canvas.transform).GetComponent(); + } + } + } + + if (shopManager != null) + { + shopManager.OpenNPCShop(npcID); + } + } } else if (idFunction == (int)SERVICE_TYPE.NPC_INSTALL) { diff --git a/Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs b/Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs index 291031739d..14d199f2bc 100644 --- a/Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs +++ b/Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs @@ -59,6 +59,16 @@ namespace BrewMonster.UI //EC_Game.GetGameRun().GetHostPlayer().EndNPCService(); EC_ManMessageMono.Instance.EC_ManPlayer.GetHostPlayer().EndNPCService(); } + + public DialogScriptTableObject GetDialogResource() + { + return m_dialogResouce; + } + + public Canvas GetCanvas() + { + return m_canvas; + } } public enum EC_GAMEUI_ICONS diff --git a/Assets/Scripts/CECUIManager.cs b/Assets/Scripts/CECUIManager.cs index 7b70d5d5ae..a2a88a3871 100644 --- a/Assets/Scripts/CECUIManager.cs +++ b/Assets/Scripts/CECUIManager.cs @@ -130,4 +130,12 @@ public class CECUIManager : MonoSingleton } return gameUI; } + + /// + /// Get the current target NPC ID (same as stored from NPCINFO event) + /// + public int GetCurrentTargetNPCID() + { + return currentTargetNPCID; + } } \ No newline at end of file