Files
test/Assets/Scripts/CECUIManager.cs
T
2026-04-30 16:29:22 +07:00

904 lines
30 KiB
C#

using BrewMonster;
using BrewMonster.Network;
using BrewMonster.UI;
using System;
using System.Collections.Generic;
using BrewMonster.PerfectWorld.Scripts.Utility.ChatFilter;
using BrewMonster.Scripts.Chat.EmotionData;
using BrewMonster.Scripts.Managers;
using BrewMonster.Scripts.Task.UI;
using BrewMonster.Scripts.UI;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using TMPro;
public class CECUIManager : MonoSingleton<CECUIManager>
{
[SerializeField] private TMP_Text _fpsText;
[SerializeField] private List<GameObject> uiPrefabs; // drag các prefab UI vào đây
private readonly Dictionary<Type, GameObject> _spawnedUIs = new();
/// <summary>Stack of dialog names (front = index 0 = top / currently on top).</summary>
[SerializeField]private List<string> _uiStack = new();
/// <summary>Max number of dialogs in stack; when exceeded on Push, the top is popped once. 0 = unlimited.</summary>
[SerializeField] private int _maxStack = 1;
[SerializeField] private HUDNPC npsUI;
[SerializeField] private int currentTargetNPCID;
CECGameUIMan gameUI;
public CECGameUIMan GameUI => gameUI;
AUIManager aUIManager;
AUIDialog _dlgPlayerOptions;
[SerializeField] private DialogScriptTableObject dialogResouce;
[SerializeField] private Canvas canvasDlg;
[SerializeField]
[Tooltip("Chat TMP: EmotionLibrarySpriteMap (SO). CECGameUIMan is not a MonoBehaviour — assign here on CECUIManager.")]
private EmotionLibrarySpriteMap _emotionLibrarySpriteMap;
[SerializeField] private UnityEngine.UI.Button btnSecondClick;
[SerializeField] CDlgQuickBar m_pDlgQuickBar1;
[SerializeField] GameObject ChangeSkillShortcutButton;
public CDlgSkillSubOther m_pDlgSkillSubOther;
CDlgMessageBox m_pDlgMessageBox;
public CDlgSkillAction m_pDlgSkillAction;
Sprite[] m_iconlistIvtr;
private CDlgInfoTooltip m_pDlgSkillTooltip;
// Task update timer / 任务更新计时器
private float _nextTaskUpdateTime = 0f;
private const float TASK_UPDATE_INTERVAL = .1f;
protected override void Awake()
{
base.Awake();
EventBus.Subscribe<CECHostPlayer.NPCINFO>(ShowUINPC);
EventBus.Subscribe<CECHostPlayer.TargetHUDClearEvent>(OnTargetHUDClear);
EventBus.Subscribe<CECHostPlayer.HostPlayerLevelUpUIEvent>(OnHostPlayerLevelUpUI);
EventBus.Subscribe<NPCDiedEvent>(TryHideUINPC);
EventBus.Subscribe<MessageBoxEvent>(OnMessageBox);
gameUI = new CECGameUIMan();
gameUI.SetDependency(dialogResouce, canvasDlg);
gameUI.SetEmotionSpriteMap(_emotionLibrarySpriteMap);
gameUI.Init();
m_pDlgSkillAction = GetComponent<CDlgSkillAction>();
// Wire up second-click button / 连接第二次点击按钮
if (btnSecondClick != null)
{
btnSecondClick.onClick.AddListener(OnSecondClickButtonClicked);
}
ShowUI("Win_Hpmpxp");
#if UNITY_EDITOR
ChangeSkillShortcutButton.SetActive(true);
#else
ChangeSkillShortcutButton.SetActive(false);
#endif
}
private void Update()
{
// _fpsText.text = $"{Mathf.RoundToInt(1f / Time.deltaTime)}";
if (m_pDlgQuickBar1 != null)
{
m_pDlgQuickBar1.UpdateShortcuts();
}
// Task UI must run from this Update(): TickInvoker often never calls CECUIManager.Tick in practice
// (registration order, DDOL instance, or no TickInvoker in scene), so logs/body there never ran.
// 任务 UI 必须在此 Update 驱动:TickInvoker 在部分场景下根本不会调到本类 Tick。
if (Time.unscaledTime < _nextTaskUpdateTime)
return;
_nextTaskUpdateTime = Time.unscaledTime + TASK_UPDATE_INTERVAL;
UpdateTaskUI();
if (EC_Game.GetGameRun()?.GetHostPlayer() != null)
EC_Game.GetGameRun()?.ShowAccountLoginInfo();
}
/// <summary>
/// Rebuild acceptable-quest cache after host levels up (heavy scan; not on player code path).
/// 升级后重建可接任务缓存(全表扫描放在 UI 层)。
/// </summary>
private void OnHostPlayerLevelUpUI(CECHostPlayer.HostPlayerLevelUpUIEvent _)
{
DlgTask.RebuildAcceptableQuestCache();
}
/// <summary>
/// Update task UI from DlgTask and DlgTaskTrace / 从DlgTask和DlgTaskTrace更新任务UI
/// </summary>
private void UpdateTaskUI()
{
try
{
var inGameUIMan = GetInGameUIMan();
if (inGameUIMan == null) return;
// Prefer cached DlgTask from Init (Win_Quest). Do not rely on `is DlgTask` on AUIDialog:
// prefab may register as AUIDialog while DlgTask is the concrete component on the same GO.
// 优先使用 Init 里缓存的 DlgTask;字典里可能是 AUIDialog 引用,`is DlgTask` 会为 false。
DlgTask dlgTask = null;
if (inGameUIMan is CECGameUIMan gameUi && gameUi.GetDlgTask() != null)
dlgTask = gameUi.GetDlgTask();
if (dlgTask == null)
{
var dlg = inGameUIMan.GetDialog(CECUIHelper.DlgTaskName);
dlgTask = dlg != null ? dlg.GetComponent<DlgTask>() : null;
}
if (dlgTask == null)
return;
if (EC_Game.GetGameRun()?.GetHostPlayer() == null)
return;
var bShowTrace = inGameUIMan.IsDialogShow("Win_QuestMinion");
dlgTask.RefreshTaskTrace(bShowTrace);
dlgTask.Tick();
}
catch (System.Exception)
{
// Silently handle errors to avoid spamming logs / 静默处理错误以避免日志刷屏
// Task UI update errors are handled silently / 任务UI更新错误被静默处理
}
}
private void OnDestroy()
{
EventBus.Unsubscribe<CECHostPlayer.NPCINFO>(ShowUINPC);
EventBus.Unsubscribe<CECHostPlayer.TargetHUDClearEvent>(OnTargetHUDClear);
EventBus.Unsubscribe<CECHostPlayer.HostPlayerLevelUpUIEvent>(OnHostPlayerLevelUpUI);
EventBus.Unsubscribe<NPCDiedEvent>(TryHideUINPC);
EventBus.Unsubscribe<MessageBoxEvent>(OnMessageBox);
}
private void ShowUINPC(CECHostPlayer.NPCINFO obj)
{
var host = EC_Game.GetGameRun()?.GetHostPlayer();
if (host != null && host.GetSelectedTarget() != obj.IDNPC)
return;
npsUI.gameObject.SetActive(true);
string hpText = obj.MaxHealth > 0 ? $"{obj.CurrentHealth}/{obj.MaxHealth}" : "-/-";
npsUI.SetText(hpText, obj.Name ?? "", "");
float fill = obj.MaxHealth > 0 ? (float)obj.CurrentHealth / (float)obj.MaxHealth : 1f;
npsUI.SetHealthImage(fill);
currentTargetNPCID = obj.IDNPC;
}
private void OnTargetHUDClear(CECHostPlayer.TargetHUDClearEvent _)
{
npsUI.gameObject.SetActive(false);
}
public CDlgQuickBar GetCDlgQuickBar()
{
return m_pDlgQuickBar1;
}
private void TryHideUINPC(NPCDiedEvent obj)
{
if (obj.NPCID != currentTargetNPCID) return;
npsUI.gameObject.SetActive(false);
}
/// <summary>
/// Show UI by name of component ("DlgTask", "EC_InventoryUI"). When isStack is true, pushes the dialog onto the UI stack (brings to front and tracks for Pop).
/// </summary>
/// <param name="componentName">Name of component ("DlgTask", "EC_InventoryUI")</param>
/// <param name="isStack">If true, push onto stack (show + SetAsLastSibling + track); if false, just show.</param>
/// <param name="hideCurrentUI">just hide current ui- not pop</param>
public AUIDialog ShowUI(string componentName)
{
if (string.IsNullOrEmpty(componentName) || canvasDlg == null)
{
if (canvasDlg == null) Debug.LogError("canvasDlg is null");
return null;
}
var currentDialogue = GetCurrentDialog();
if (currentDialogue != null)
{
if (_uiStack.Count > 0 && string.Compare(_uiStack[0], componentName, StringComparison.Ordinal) == 0)
{
return currentDialogue;
}
currentDialogue.Show(false);
}
return Push(componentName);
}
public void HideCurrentUIInStack()
{
Pop();
//show next one
var currentDialogue = GetCurrentDialog();
if (currentDialogue != null)
{
currentDialogue.Show(true);
}
}
/// <summary>
/// Push a dialog onto the UI stack: show it, add to front of stack, and SetAsLastSibling so it draws on top. If already in stack, moves it to front.
/// </summary>
private AUIDialog Push(string componentName)
{
if (string.IsNullOrEmpty(componentName) || canvasDlg == null) return null;
_uiStack.Remove(componentName);
if (_maxStack > 0 && _uiStack.Count >= _maxStack)
Pop();
var dlg = GetInGameUIMan().GetDialog(componentName);
if (dlg == null) return null;
dlg.Show(true);
_uiStack.Insert(0, componentName);
dlg.transform.SetAsLastSibling();
return dlg;
}
/// <summary>
/// Pop the top dialog off the stack: hide it and bring the new top (if any) to front. Returns true if something was popped.
/// </summary>
private bool Pop()
{
if (_uiStack.Count == 0) return false;
string top = _uiStack[0];
_uiStack.RemoveAt(0);
var dlg = GetInGameUIMan().GetDialog(top);
if (dlg != null)
dlg.Show(false);
if (_uiStack.Count > 0)
{
var newTop = GetInGameUIMan().GetDialog(_uiStack[0]);
if (newTop != null)
newTop.transform.SetAsLastSibling();
}
return true;
}
/// <summary>Returns the name of the dialog currently on top of the stack, or null if stack is empty.</summary>
public string GetCurrentUI()
{
if (_uiStack.Count == 0) return null;
return _uiStack[0];
}
/// <summary>Returns the dialog instance currently on top of the stack, or null if stack is empty.</summary>
public AUIDialog GetCurrentDialog()
{
var currentUI = GetCurrentUI();
if (currentUI == null) return null;
return GetInGameUIMan().GetDialog(currentUI);
}
/// <summary>Returns the number of dialogs in the stack (0 when empty).</summary>
public int GetStackCount() => _uiStack.Count;
/// <summary>Returns true if the given dialog name is in the stack.</summary>
public bool IsInStack(string componentName)
{
return !string.IsNullOrEmpty(componentName) && _uiStack.Contains(componentName);
}
/// <summary>Returns the dialog name at the given index (0 = top) without removing. Returns null if index is out of range.</summary>
public string PeekStack(int index = 0)
{
if (index < 0 || index >= _uiStack.Count) return null;
return _uiStack[index];
}
/// <summary>Clears the entire stack. If hideAll is true, hides every dialog in the stack before clearing.</summary>
public void ClearStack(bool hideAll = true)
{
if (hideAll)
{
var gui = GetInGameUIMan();
foreach (string name in _uiStack)
{
var dlg = gui?.GetDialog(name);
if (dlg != null) dlg.Show(false);
}
}
_uiStack.Clear();
}
public CDlgMessageBox ShowMessageBoxGeneral(string title, string message, AUIDialog dlg)
{
var msgBox = GetInGameUIMan().GetDialog("DlgMessageBox") as CDlgMessageBox;
if (msgBox != null)
{
msgBox.ShowMessageBoxGeneral(title, message, dlg);
return msgBox;
}
Debug.LogError("DlgMessageBox not found in InGameUIMan");
return null;
}
public CDlgMessageBox ShowMessageBoxYes(string title, string message, AUIDialog dlg, Action onClickedYes)
{
var msgBox = GetInGameUIMan().GetDialog("DlgMessageBox") as CDlgMessageBox;
if (msgBox != null)
{
msgBox.ShowMessageBoxYes(title, message, dlg, onClickedYes);
return msgBox;
}
Debug.LogError("DlgMessageBox not found in InGameUIMan");
return null;
}
public CDlgMessageBox ShowMessageBoxNo(string title, string message, AUIDialog dlg, Action onClickedNo)
{
var msgBox = GetInGameUIMan().GetDialog("DlgMessageBox") as CDlgMessageBox;
if (msgBox != null)
{
msgBox.ShowMessageBoxNo(title, message, dlg, onClickedNo);
return msgBox;
}
Debug.LogError("DlgMessageBox not found in InGameUIMan");
return null;
}
public CDlgMessageBox ShowMessageBoxYesAndNo(string title, string message, AUIDialog dlg, Action onClickedYes, Action onClickedNo)
{
var msgBox = GetInGameUIMan().GetDialog("DlgMessageBox") as CDlgMessageBox;
if (msgBox != null)
{
msgBox.ShowMessageBoxYesAndNo(title, message, dlg,onClickedYes, onClickedNo);
return msgBox;
}
Debug.LogError("DlgMessageBox not found in InGameUIMan");
return null;
}
/// <summary>
/// Dialog names on <see cref="_uiStack"/> that are normal gameplay HUD and must NOT block world raycasts
/// (the stack always has at least <c>Win_Hpmpxp</c> after <see cref="Awake"/>).
/// </summary>
private static readonly HashSet<string> HudStackDialogNames = new HashSet<string>(StringComparer.Ordinal)
{
"Win_Hpmpxp",
};
/// <summary>
/// Returns true when a modal / non-HUD dialog is open and world clicks should be ignored.
/// </summary>
public bool IsWorldInteractionBlockedByUI()
{
if (_uiStack == null || _uiStack.Count == 0)
return false;
foreach (var name in _uiStack)
{
if (string.IsNullOrEmpty(name))
continue;
if (!HudStackDialogNames.Contains(name))
return true;
}
return false;
}
/// <summary>
/// True when the pointer hits UI that should consume the click (buttons, fields, scroll views).
/// Unlike <see cref="EventSystem.IsPointerOverGameObject"/>, full-screen Images/Text with raycast
/// but no interactive control do not block — so clicks can reach the world for NPC selection.
/// </summary>
public static bool IsPointerOverInteractiveUI()
{
var es = EventSystem.current;
if (es == null)
return false;
Vector2 pos = Input.mousePosition;
if (Input.touchCount > 0)
pos = Input.GetTouch(0).position;
var ped = new PointerEventData(es) { position = pos };
var results = new List<RaycastResult>();
es.RaycastAll(ped, results);
if (results.Count == 0)
return false;
foreach (var r in results)
{
var go = r.gameObject;
if (go == null)
continue;
if (go.GetComponentInParent<Selectable>() != null)
return true;
if (go.GetComponentInParent<TMP_InputField>() != null)
return true;
if (go.GetComponentInParent<InputField>() != null)
return true;
if (go.GetComponentInParent<ScrollRect>() != null)
return true;
}
return false;
}
// public CDlgMessageBox ShowMessageBox(MessageBoxData messageBoxData)
// {
// var msgBox = GetInGameUIMan().GetDialog("DlgMessageBox") as CDlgMessageBox;
// if (msgBox != null)
// {
// msgBox.ShowMessageBox(messageBoxData);
// return msgBox;
// }
// else
// {
// Debug.LogError("DlgMessageBox not found in InGameUIMan");
// }
//
// return null;
// }
// public CDlgMessageBox ShowMessageBox(string title, string message, MessageBoxType messageBoxType,
// Action onClickedYes = null, Action onClickedNo = null)
// {
// var msgBox = GetInGameUIMan().GetDialog("DlgMessageBox") as CDlgMessageBox;
// if (msgBox != null)
// {
// msgBox.ShowMessageBox(new MessageBoxData()
// {
// Title = title,
// Message = message,
// MessageBoxType = messageBoxType,
// OnClickedYes = onClickedYes,
// OnClickedNo = onClickedNo
// });
// return msgBox;
// }
// else
// {
// Debug.LogError("DlgMessageBox not found in InGameUIMan");
// }
//
// return null;
// }
public void UpdateSkillRelatedUI()
{
// ¸üм¼ÄÜÏà¹ØµÄ½çÃæÏÔʾ
if (m_pDlgQuickBar1)
m_pDlgQuickBar1.UpdateShortcuts();
/* if (m_pDlgSkillEdit != null && m_pDlgSkillEdit->IsShow())
{
// ¼¼Äܱ༭½çÃæÖ»ÔÚ Show(true) µÄʱºò²ÅÄܸüÐÂ
m_pDlgSkillEdit->Show(false);
}*/
}
/// <summary>
/// Show skill tooltip with description near the clicked skill button.
/// </summary>
/// <param name="hintText">The skill description and requirements</param>
/// <param name="sourceButton">The button RectTransform to position near</param>
public void ShowSkillTooltip(string hintText, RectTransform sourceButton, Action triggerAction = null)
{
if (string.IsNullOrEmpty(hintText))
{
Debug.LogWarning("[CECUIManager] ShowSkillTooltip called with empty hint text");
return;
}
// Lazy-load the tooltip dialog
if (m_pDlgSkillTooltip == null)
{
var tooltip = GetInGameUIMan().GetDialog("CDlgInfoTooltip") as CDlgInfoTooltip;
if (tooltip != null)
{
m_pDlgSkillTooltip = tooltip;
}
else
{
Debug.LogWarning(
"[CECUIManager] CDlgSkillTooltip not found in dialog resource. Tooltip will not be displayed.");
return;
}
}
if (m_pDlgSkillTooltip != null)
{
m_pDlgSkillTooltip.ShowTooltip(hintText, sourceButton, triggerAction);
}
}
/// <summary>
/// Hide the skill tooltip if it's currently visible.
/// </summary>
public void HideSkillTooltip()
{
if (m_pDlgSkillTooltip != null)
{
m_pDlgSkillTooltip.HideTooltip();
}
}
private void OnMessageBox(MessageBoxEvent messageBoxEvent)
{
BMLogger.LogError("CECUIManager OnMessageBox called");
int iRetVal = messageBoxEvent.iRetVal;
AUIDialog pDlg = messageBoxEvent.pDlg;
if (pDlg == m_pDlgMessageBox)
{
OnNewMessageBox(iRetVal);
return;
}
UnityGameSession pSession = UnityGameSession.Instance;
CECHostPlayer pHost = CECGameRun.Instance.GetHostPlayer();
if (string.Equals(pDlg.GetName(), "Game_Quit", StringComparison.OrdinalIgnoreCase))
{
// TODO
// Cancel hotkey customize because hot key state is determinted by CECHostInputFilter
// which is shared between repick role
//
// if (m_pDlgSettingQuickKey->IsShow())
// m_pDlgSettingQuickKey->Show(false);
if( pSession.GameSession.IsConnected )
{
// TODO
// if (CECCrossServer::Instance().IsOnSpecialServer())
// g_pGame->GetGameRun()->GetPendingLogOut().AppendForSaveConfig(new CECPendingLogoutCrossServer());
// else
// g_pGame->GetGameRun()->GetPendingLogOut().AppendForSaveConfig(new CECPendingLogoutHalf());
EC_Game.GetGameRun().GetPendingLogOut().AppendForSaveConfig(new CECPendingLogoutHalf());
}
else
EC_Game.GetGameRun().SetLogoutFlag(2);
}
else if ((string.Equals(pDlg.GetName(), "Game_TeachSkill", StringComparison.OrdinalIgnoreCase) &&
DialogBoxCommandIDs.IDOK == iRetVal) ||
(string.Equals(pDlg.GetName(), "Game_LearnSkill", StringComparison.OrdinalIgnoreCase) &&
DialogBoxCommandIDs.IDOK == iRetVal))
{
if (string.Equals(pDlg.GetName(), "Game_TeachSkill", StringComparison.OrdinalIgnoreCase))
{
}
else if (string.Equals(pDlg.GetName(), "Game_LearnSkill", StringComparison.OrdinalIgnoreCase))
{
/* int skillID = (int)pDlg.GetData();
int nCondition = pHost.CheckSkillLearnCondition(skillID, true);
if (0 == nCondition)
{
UnityGameSession.c2s_SendCmdNPCSevLearnSkill(skillID);
}*/
/*else if (1 == nCondition)
AddChatMessage(GetStringFromTable(270), GP_CHAT_MISC);
else if (6 == nCondition)
AddChatMessage(GetStringFromTable(527), GP_CHAT_MISC);
else if (7 == nCondition)
AddChatMessage(GetStringFromTable(541), GP_CHAT_MISC);
else if (8 == nCondition)
AddChatMessage(GetStringFromTable(271), GP_CHAT_MISC);
else if (9 == nCondition)
AddChatMessage(GetStringFromTable(556), GP_CHAT_MISC);
else if (10 == nCondition)
AddChatMessage(GetStringFromTable(557), GP_CHAT_MISC);
else if (12 == nCondition)
AddChatMessage(GetStringFromTable(11168), GP_CHAT_MISC);*/
}
}
//else if (pDlg is DlgInstall dlgInstall && dlgInstall.GetInstallMode == DlgInstall.InstallMode.Disenchase &&
// DialogBoxCommandIDs.IDOK == iRetVal)
//{
// UnityGameSession.c2s_CmdNPCSevClearEmbeddedChip(dlgInstall.FirstSlotIndex,
// dlgInstall.SelectedEquip.GetTemplateID());
// UnityGameSession.c2s_CmdGetAllData(true, true, false);
// dlgInstall.Show(false);
// pHost.EndNPCService();
// // m_pCurNPCEssence = NULL;
// // m_pDlgInventory->Show(false);
// pHost.GetPack((int)InventoryType.IVTRTYPE_PACK).UnfreezeAllItems();
// ShowMessageBoxGeneral("", pDlg.GetStringFromTable(228), null);
//}
}
private bool OnNewMessageBox(int iRetVal)
{
return true;
}
// private System.Type FindTypeByName(string componentName)
// {
// string[] namespacePrefixes = {
// "", // No namespace
// "BrewMonster.Scripts.Task.UI.",
// "BrewMonster.UI.",
// "BrewMonster.Scripts.UI.",
// "BrewMonster."
// };
//
// // Try with common namespace prefixes
// foreach (var prefix in namespacePrefixes)
// {
// string fullTypeName = string.IsNullOrEmpty(prefix) ? componentName : prefix + componentName;
// var type = System.Type.GetType(fullTypeName);
// if (type != null) return type;
// }
//
// // Search in all assemblies
// foreach (var assembly in System.AppDomain.CurrentDomain.GetAssemblies())
// {
// var type = assembly.GetType(componentName);
// if (type != null) return type;
//
// foreach (var prefix in namespacePrefixes)
// {
// if (string.IsNullOrEmpty(prefix)) continue;
// type = assembly.GetType(prefix + componentName);
// if (type != null) return type;
// }
// }
//
// return null;
// }
//
// private bool TryShowCachedUI(System.Type type)
// {
// if (type != null && _spawnedUIs.TryGetValue(type, out var uiGo))
// {
// uiGo.SetActive(true);
// return true;
// }
// return false;
// }
//
// private bool FindUIByName(string componentName, System.Type type)
// {
// for (int i = 0; i < canvasDlg.transform.childCount; i++)
// {
// var child = canvasDlg.transform.GetChild(i);
// if (!child.name.Contains(componentName) && child.name != componentName) continue;
//
// if (type != null)
// {
// var foundComponent = child.GetComponentInChildren(type, true);
// if (foundComponent != null)
// {
// ActivateAndCacheUI(foundComponent.gameObject, type);
// return true;
// }
// }
//
// ActivateAndCacheUI(child.gameObject, type);
// return true;
// }
// return false;
// }
//
// private bool FindUIByType(System.Type type)
// {
// if (type == null) return false;
//
// var foundComponent = canvasDlg.GetComponentInChildren(type, true);
// if (foundComponent != null)
// {
// ActivateAndCacheUI(foundComponent.gameObject, type);
// return true;
// }
// return false;
// }
private void ActivateAndCacheUI(GameObject uiGameObject, System.Type type)
{
uiGameObject.SetActive(true);
if (type != null) _spawnedUIs[type] = uiGameObject;
}
/// <summary>
/// Ẩn UI (disable thay vì destroy)
/// </summary>
// public void HideUI<T>() where T : Component
// {
// var type = typeof(T);
// if (_spawnedUIs.TryGetValue(type, out var uiGo))
// {
// uiGo.SetActive(false);
// }
// }
/// <summary>
/// Kiểm tra UI có đang active không
/// </summary>
public bool IsUIActive<T>() where T : Component
{
var type = typeof(T);
return _spawnedUIs.TryGetValue(type, out var uiGo) && uiGo.activeSelf;
}
/// <summary>
/// Ẩn tất cả UI hiện tại
/// </summary>
public void HideAll()
{
foreach (var kv in _spawnedUIs)
{
kv.Value.SetActive(false);
}
}
public CECGameUIMan GetInGameUIMan()
{
if (gameUI == null)
{
gameUI = new CECGameUIMan();
gameUI.SetDependency(dialogResouce, canvasDlg);
gameUI.SetEmotionSpriteMap(_emotionLibrarySpriteMap);
gameUI.Init();
}
return gameUI;
}
public void GetIconStateMgr()
{
}
/// <summary>Shows the player options menu for the given character. Requires a prefab with id "DlgPlayerOptions" in DialogScriptTableObject.</summary>
public void ShowPlayerOptionsDialog(int characterId, Vector2 position)
{
if (characterId == 0 || canvasDlg == null) return;
var gui = GetInGameUIMan();
if (_dlgPlayerOptions == null)
_dlgPlayerOptions = gui.GetDialog("DlgPlayerOptions");
_dlgPlayerOptions?.ShowForPlayer(characterId);
_dlgPlayerOptions.transform.position = position;
}
/// <summary>
/// Opens <c>Win_Award</c> (<see cref="CDlgAward"/>) and fills it from equipped template ids using local item data
/// (<see cref="CDlgAward.TestItemInInventory"/>). For debugging local prop/description behavior.
/// </summary>
/// <returns>The dialog instance, or null if the prefab is not registered.</returns>
public void ShowDlgAwardEquippedItemsLocalTest()
{
const string dlgAwardName = "Win_Award";
CDlgAward award = null;
if (GetInGameUIMan().GetDialog(dlgAwardName) is CDlgAward dlgAward)
{
award = dlgAward;
}
var shown = ShowUI(dlgAwardName) as CDlgAward;
(shown ?? award).TestItemInInventory();
}
/// <summary>
/// Get the current target NPC ID (same as stored from NPCINFO event)
/// </summary>
public int GetCurrentTargetNPCID()
{
return currentTargetNPCID;
}
//todo: change this code to other place
private int slot = 0;
public Sprite[] IconlistIvtr
{
get
{
if (m_iconlistIvtr == null || m_iconlistIvtr.Length == 0)
{
m_iconlistIvtr = Resources.LoadAll<Sprite>("UI/IconSprites/iconlist_ivtrm_multisprite");
}
return m_iconlistIvtr;
}
}
public Sprite GetSpriteInListIvtr(string name)
{
foreach (var item in IconlistIvtr)
{
if (item.name.Equals(name))
{
return item;
}
}
return null;
}
public void OnClickedWaveHand()
{
// if (EC_Game.GetGameRun().GetPoseCmdShortcuts() == null)
// {
// EC_Game.GetGameRun().StartGame(0, Vector3.zero);
// }
CECShortcut pSC = EC_Game.GetGameRun().GetPoseCmdShortcuts().GetShortcut(slot);
if (pSC != null) // && pObjSrc->GetDataPtr("ptr_CECShortcut") == pSC
{
// a_LogOutput(1, "[Dat Emote] ptr_CECShortcut");
pSC.Execute();
}
}
public void OnClickFly()
{
CECHostPlayer hostPlayer = EC_Game.GetGameRun()?.GetHostPlayer();
if (hostPlayer != null)
{
hostPlayer.CmdFly(true);
}
}
public void OnChangeSkillShortcut()
{
#if UNITY_EDITOR
CECHostPlayer hostPlayer = EC_Game.GetGameRun()?.GetHostPlayer();
if (hostPlayer != null)
{
hostPlayer.CycleSkillShortcuts();
}
#endif
}
/// <summary>
/// Handle second-click button click - triggers attack or move to NPC / 处理第二次点击按钮 - 触发攻击或移动到NPC
/// Uses OnMsgLBtnClick with selected target to reuse existing logic / 使用OnMsgLBtnClick和选中的目标来重用现有逻辑
/// </summary>
private void OnSecondClickButtonClicked()
{
CECHostPlayer hostPlayer = EC_Game.GetGameRun()?.GetHostPlayer();
if (hostPlayer != null)
{
int selectedTarget = hostPlayer.GetSelectedTarget();
if (selectedTarget != 0)
{
// Call OnMsgLBtnClick with the selected target to simulate second click / 使用选中的目标调用OnMsgLBtnClick以模拟第二次点击
hostPlayer.OnMsgLBtnClick(selectedTarget);
}
}
}
public struct MessageBoxEvent
{
public int iRetVal;
public AUIDialog pDlg;
public MessageBoxEvent(int retVal, AUIDialog dlg)
{
iRetVal = retVal;
pDlg = dlg;
}
}
public void FilterBadWords(ref string str)
{
str = ChatFilterService.Filter(str, out var isValidWord);
}
}