diff --git a/Assets/PerfectWorld/Scripts/Chat/Chat_GameSession.cs b/Assets/PerfectWorld/Scripts/Chat/Chat_GameSession.cs index 5c46495b68..84fce2a20f 100644 --- a/Assets/PerfectWorld/Scripts/Chat/Chat_GameSession.cs +++ b/Assets/PerfectWorld/Scripts/Chat/Chat_GameSession.cs @@ -1,11 +1,7 @@ using System; using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Text; using BrewMonster.Network; using BrewMonster.Scripts.UI; -using BrewMonster.UI; -using CSNetwork; using CSNetwork.GPDataType; using CSNetwork.Protocols; @@ -207,356 +203,6 @@ namespace BrewMonster.Scripts.Chat return bOK; } - public static bool HanldeGPChatSystem(chatmessage p, bool bCalledagain) - { - /*string strMsg = ""; - switch (p.Srcroleid) - { - case 1: //CMSG_BIDSTART - case 2: //CMSG_BIDEND - case 3: //CMSG_BATTLESTART - case 4: //CMSG_BATTLEEND - case 6: //CMSG_BONUSSEND - case 7: //CMSG_SPECIAL - { - if (!bCalledagain) - { - List pending; - if (!OnBattleChatMessage(p, pending)) - { - if (pending.GetSize() > 0) - { - GetFactionInfo(pending.GetSize(), pending.GetData()); - m_aPendingProtocols.Add(pProtocol); - return false; - } - } - } - else - { - if (!OnBattleChatMessage(p, NULL)) - { - return false; - } - } - - return true; - } - } - - return true; - } - - public static bool OnBattleChatMessage(chatmessage p, AArray pPendingFactions) - { - var pGameUI = g_pGame.GetGameRun().GetUIManager().GetInGameUIMan(); - var pDlgGuildMap = (CDlgGuildMap)pGameUI.GetDialog("Win_GuildMap"); - - var info = GlobalData.GetDomainInfos(); - var pMan = g_pGame.GetFactionMan(); - - string strMsg = string.Empty; - - if (pPendingFactions != null) - { - pPendingFactions.RemoveAll(); - } - - switch (p.srcroleid) - { - case 1: // CMSG_BIDSTART - { - strMsg = pGameUI.GetStringFromTable(4001); - break; - } - - case 2: // CMSG_BIDEND - { - int group = p.msg.Size / (sizeof(short) + 2 * sizeof(uint)); - strMsg = pGameUI.GetStringFromTable(4002); - - if (group == 0) - break; - - int idFaction = g_pGame.GetGameRun().GetHostPlayer().GetFactionID(); - - string strAttacker = ""; - string strDefender = ""; - string strAnnounce = ""; - - pDlgGuildMap.ClearMessage(); - - var o = new OctetsStream(p.msg); - - for (; group > 0; group--) - { - short id = o.ReadShort(); - uint owner = o.ReadUInt(); - uint challenger = o.ReadUInt(); - - var finfo = pMan.GetFaction(challenger); - - if ((int)challenger == idFaction) - strAttacker = pGameUI.GetStringFromTable(4500); - else if (finfo != null) - strAttacker = string.Format(pGameUI.GetStringFromTable(4499), finfo.GetName()); - else if (pPendingFactions != null) - pPendingFactions.UniquelyAdd((int)challenger); - - if (string.IsNullOrEmpty(strAttacker)) - { - strMsg = ""; - continue; - } - - for (int i = 0; i < info.Count; i++) - { - if (info[i].id == id) - { - if (info[i].id_owner != 0) - { - if (info[i].id_owner == idFaction) - strDefender = pGameUI.GetStringFromTable(4500); - else - { - var finfo2 = pMan.GetFaction(info[i].id_owner); - - if (finfo2 != null) - strDefender = string.Format( - pGameUI.GetStringFromTable(4499), - finfo2.GetName()); - else if (pPendingFactions != null) - pPendingFactions.UniquelyAdd(info[i].id_owner); - } - } - - if (string.IsNullOrEmpty(strDefender)) - { - strAnnounce = string.Format( - pGameUI.GetStringFromTable(4511), - strAttacker, - info[i].name); - } - else - { - strAnnounce = string.Format( - pGameUI.GetStringFromTable(4512), - strAttacker, - strDefender, - info[i].name); - } - - if (pPendingFactions == null || pPendingFactions.GetSize() == 0) - { - pDlgGuildMap.AddMessage(strAnnounce); - } - - break; - } - } - - strAttacker = ""; - strDefender = ""; - } - - if ((pPendingFactions != null && pPendingFactions.GetSize() > 0) || string.IsNullOrEmpty(strMsg)) - { - pDlgGuildMap.ClearMessage(); - strMsg = ""; - } - - break; - } - - case 3: // CMSG_BATTLESTART - { - if (p.msg.Size < 2) - return false; - - var os = new OctetsStream(p.msg); - - byte id = os.ReadByte(); - byte result = os.ReadByte(); - - for (int i = 0; i < info.Count; i++) - { - if (info[i].id == id) - { - strMsg = string.Format( - pGameUI.GetStringFromTable(4003), - info[i].name); - - strMsg += pGameUI.GetStringFromTable(3900 + result); - break; - } - } - - break; - } - - case 4: // CMSG_BATTLEEND - { - if (p.msg.Size < 1) - return false; - - var os = new OctetsStream(p.msg); - - byte id = os.ReadByte(); - - for (int i = 0; i < info.Count; i++) - { - if (info[i].id == id) - { - strMsg = string.Format( - pGameUI.GetStringFromTable(4004), - info[i].name); - break; - } - } - - break; - } - - case 6: // CMSG_BONUSSEND - { - if (p.msg.Size < sizeof(uint) * 3) - return false; - - var os = new OctetsStream(p.msg); - - uint money = os.ReadUInt(); - uint id = os.ReadUInt(); - uint count = os.ReadUInt(); - - var item = CECIvtrItem.CreateItem(id, 0, count); - - strMsg = string.Format( - pGameUI.GetStringFromTable(4006), - item.GetName(), - item.GetCount()); - - break; - } - - case 7: // CMSG_SPECIAL - { - if (p.msg.Size < sizeof(short)) - return false; - - var os = new OctetsStream(p.msg); - - short id = os.ReadShort(); - - for (int i = 0; i < info.Count; i++) - { - if (info[i].id == id) - { - uint fid = info[i].id_owner; - - if (fid > 0) - { - var finfo = pMan.GetFaction(fid); - - if (finfo != null) - { - strMsg = string.Format( - pGameUI.GetStringFromTable(4007), - info[i].name, - info[i].name, - finfo.GetName()); - } - else if (pPendingFactions != null) - { - pPendingFactions.UniquelyAdd((int)fid); - } - } - else - { - strMsg = string.Format( - pGameUI.GetStringFromTable(4005), - info[i].name); - } - - break; - } - } - - break; - } - - default: - return false; - } - - if ((pPendingFactions != null && pPendingFactions.GetSize() > 0) || string.IsNullOrEmpty(strMsg)) - { - return false; - } - - g_pGame.GetGameRun().AddChatMessage( - strMsg, - ChatChannel.GP_CHAT_BROADCAST, - p.srcroleid, - null, - 1, - p.emotion);*/ - - return true; - } - - /*public static void OnTaskChatMessage(byte[] buffer, int size) - { - const int iNameLen = 20; - - int expectedSize = 4 * 5 + iNameLen * 2; - - if (size < expectedSize) - return; - - int offset = 0; - - int self_id = BitConverter.ToInt32(buffer, offset); offset += 4; - uint task_id = BitConverter.ToUInt32(buffer, offset); offset += 4; - int channel = BitConverter.ToInt32(buffer, offset); offset += 4; - int param = BitConverter.ToInt32(buffer, offset); offset += 4; - int map_id = BitConverter.ToInt32(buffer, offset); offset += 4; - - // name (UTF16 20 chars) - string name = Encoding.Unicode.GetString(buffer, offset, iNameLen * 2); - name = name.TrimEnd('\0'); - - var pTempl = EC_Game.GetTaskTemplateMan() - .GetTaskTemplByID(task_id); - - if (pTempl == null) - return; - - var tribute = pTempl.GetTribute(); - - if (string.IsNullOrEmpty(tribute)) - return; - - // register player name - EC_Game.GetGameRun().AddPlayerName(self_id, name); - - string strMsg = tribute; - - // replace $name - strMsg = strMsg.Replace("$name", $"&{name}&"); - - // replace $map - string strMap = ""; - - var instance = EC_Game.GetGameRun().GetInstance(map_id); - - if (instance != null) - strMap = instance.GetName(); - - strMsg = strMsg.Replace("$map", strMap); - - EC_Game.GetGameRun().AddChatMessage(strMsg, channel); - }*/ - public class AUICTranslate { protected string m_AString = string.Empty; diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs index 133a41e3c3..a9f3b69da8 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs @@ -1694,7 +1694,6 @@ namespace CSNetwork BitConverter.GetBytes(cmd).CopyTo(bytes, 0); BitConverter.GetBytes(cardId).CopyTo(bytes, 2); - Debug.Log($"[Cuong] SendChatData {bytes}"); p.Data.Replace(bytes); } } @@ -1743,6 +1742,7 @@ namespace CSNetwork } } EventBus.PublishChannel(pHost.GetCharacterID(), new EventChatMessageOnTopPlayer(pHost.GetCharacterID(), szMsg)); + EventBus.Publish(new ChatMessageEvent(szMsg, p.Channel)); } } diff --git a/Assets/Scripts/CECUIManager.cs b/Assets/Scripts/CECUIManager.cs index 4b2cb79795..fdcd0f9603 100644 --- a/Assets/Scripts/CECUIManager.cs +++ b/Assets/Scripts/CECUIManager.cs @@ -24,6 +24,7 @@ public class CECUIManager : MonoSingleton [SerializeField] private int currentTargetNPCID; CECGameUIMan gameUI; + public CECGameUIMan GameUI => gameUI; AUIManager aUIManager; AUIDialog _dlgPlayerOptions; [SerializeField] private DialogScriptTableObject dialogResouce; diff --git a/Assets/Scripts/ChatCommandProcessor.cs b/Assets/Scripts/ChatCommandProcessor.cs new file mode 100644 index 0000000000..947e684889 --- /dev/null +++ b/Assets/Scripts/ChatCommandProcessor.cs @@ -0,0 +1,231 @@ +/* +using UnityEngine; +using System.Collections.Generic; +using BrewMonster.Network; +using CSNetwork.GPDataType; + +namespace BrewMonster.Scripts.ChatUI +{ + public class ChatCommandProcessor + { + private struct ChatHistoryEntry + { + public ChatChannel channel; + public float time; + public string message; + } + + private static List _history = new List(); + private const int MAX_HISTORY = 10; + private static float _lastFarCryTime = 0; + private static float _lastSuperFarCryTime = 0; + + public struct ProcessResult + { + public bool success; + public ChatChannel channel; + public string message; + public string targetPlayer; // For whisper + public int itemPack; + public int itemSlot; + public string errorString; + public bool showConfirmation; + public int confirmationStringId; + } + + public static ProcessResult Process(string text, int itemPack = -1, int itemSlot = -1) + { + ProcessResult result = new ProcessResult + { + success = false, + channel = ChatChannel.GP_CHAT_LOCAL, + message = text, + itemPack = itemPack, + itemSlot = itemSlot, + showConfirmation = false + }; + + if (string.IsNullOrWhiteSpace(text)) + { + return result; + } + + // Flood protection + float now = Time.time; + if (_history.Count > 0) + { + var last = _history[_history.Count - 1]; + if (now - last.time <= 1.0f) + { + result.errorString = GetStringFromTable(272); + return result; + } + + foreach (var entry in _history) + { + if (entry.channel != ChatChannel.GP_CHAT_WHISPER && + entry.message == text && + now - entry.time <= 6.0f) + { + result.errorString = GetStringFromTable(273); + return result; + } + } + } + + // Debug commands + if (text == "##debug") + { + // Toggle console logic here if needed + result.success = true; + return result; + } + + string pszMsg = text; + ChatChannel channel = ChatChannel.GP_CHAT_LOCAL; + + if (text.Length >= 2 && text.StartsWith("!!")) + { + channel = ChatChannel.GP_CHAT_TEAM; + pszMsg = text.Substring(2); + } + else if (text.Length >= 2 && text.StartsWith("!~")) + { + channel = ChatChannel.GP_CHAT_FACTION; + pszMsg = text.Substring(2); + } + else if (text.Length >= 2 && text.StartsWith("!@")) + { + channel = ChatChannel.GP_CHAT_FARCRY; + pszMsg = text.Substring(2); + + // Requirement check + int itemNum = GetPlayerItemCount(12979) + GetPlayerItemCount(36092); + if (itemNum < 1 || GetPlayerLevel() < 5) + { + result.errorString = GetStringFromTable(731); + return result; + } + if (now - _lastFarCryTime <= 1.0f) + { + result.errorString = GetStringFromTable(730); + return result; + } + + result.showConfirmation = true; + result.confirmationStringId = 732; + } + else if (text.Length >= 2 && text.StartsWith("!#")) + { + channel = ChatChannel.GP_CHAT_SUPERFARCRY; + pszMsg = text.Substring(2); + + int itemNum = GetPlayerItemCount(27728) + GetPlayerItemCount(27729); + if (itemNum < 1) + { + result.errorString = GetStringFromTable(8531); + return result; + } + if (now - _lastSuperFarCryTime <= 1.0f) + { + result.errorString = GetStringFromTable(8530); + return result; + } + + result.showConfirmation = true; + result.confirmationStringId = 8532; + } + else if (text.Length >= 1 && text.StartsWith("$")) + { + if (HasCountryChannel()) + { + channel = ChatChannel.GP_CHAT_COUNTRY; + pszMsg = text.Substring(1); + // Add king/money checks if needed + } + else + { + channel = ChatChannel.GP_CHAT_TRADE; + pszMsg = text.Substring(1); + if (GetPlayerLevel() <= 30) + { + result.errorString = GetStringFromTable(530); + return result; + } + } + } + else if (text.Length >= 1 && text.StartsWith("/")) + { + string cmdPart = text.Substring(1); + int spaceIdx = cmdPart.IndexOf(' '); + if (spaceIdx > 0) + { + result.targetPlayer = cmdPart.Substring(0, spaceIdx); + pszMsg = cmdPart.Substring(spaceIdx + 1); + channel = ChatChannel.GP_CHAT_WHISPER; + } + else + { + // Maybe just showing help or something + result.errorString = GetStringFromTable(234); + return result; + } + } + else + { + channel = HasCountryWarChannel() ? ChatChannel.GP_CHAT_BATTLE : ChatChannel.GP_CHAT_LOCAL; + pszMsg = text; + } + + if (string.IsNullOrWhiteSpace(pszMsg)) + { + return result; + } + + result.success = true; + result.channel = channel; + result.message = pszMsg; + + // Update history + if (_history.Count >= MAX_HISTORY) + _history.RemoveAt(0); + + _history.Add(new ChatHistoryEntry { channel = channel, time = now, message = text }); + + if (channel == ChatChannel.GP_CHAT_FARCRY) _lastFarCryTime = now; + if (channel == ChatChannel.GP_CHAT_SUPERFARCRY) _lastSuperFarCryTime = now; + + return result; + } + + private static string GetStringFromTable(int id) + { + return CECUIManager.Instance?.GetInGameUIMan()?.GetStringFromTable(id) ?? $"[STR_{id}]"; + } + + private static int GetPlayerItemCount(int tid) + { + return 0; + //return EC_GameRun.Instance?.GetHostPlayer()?.GetPack()?.GetItemTotalNum(tid) ?? 0; + } + + private static int GetPlayerLevel() + { + return 0; + //return EC_GameRun.Instance?.GetHostPlayer()?.GetBasicProps()?.iLevel ?? 0; + } + + private static bool HasCountryChannel() + { + return false; + //return EC_GameRun.Instance?.GetHostPlayer()?.HasCountryChannel() ?? false; + } + + private static bool HasCountryWarChannel() + { + return false; + //return EC_GameRun.Instance?.GetHostPlayer()?.HasCountryWarChannel() ?? false; + } + } +} +*/ diff --git a/Assets/Scripts/ChatCommandProcessor.cs.meta b/Assets/Scripts/ChatCommandProcessor.cs.meta new file mode 100644 index 0000000000..bd0aeb04ed --- /dev/null +++ b/Assets/Scripts/ChatCommandProcessor.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 3a090b5a4bce36b44b6def3542c520d4 \ No newline at end of file diff --git a/Assets/Scripts/ChatInputHandler.cs b/Assets/Scripts/ChatInputHandler.cs index d274305e5e..f1c814e0b1 100644 --- a/Assets/Scripts/ChatInputHandler.cs +++ b/Assets/Scripts/ChatInputHandler.cs @@ -1,267 +1,367 @@ using UnityEngine; using TMPro; -using BrewMonster; -using BrewMonster.Network; using CSNetwork.GPDataType; -using System; using System.Collections.Generic; -using System.Text; -using BrewMonster.Scripts.Task; +using BrewMonster.Network; +using BrewMonster.UI; +using CSNetwork; namespace BrewMonster.Scripts.ChatUI { - /// - /// Handles chat input from TMP_InputField and provides task-sharing functionality. - /// Converted from CDlgTaskTrace::OnCommand_Chat (DlgTaskTrace.cpp line 888). - /// public class ChatInputHandler : MonoBehaviour { - [Header("UI References")] - public TMP_InputField inputField; // Ô gõ text + public TMP_InputField inputField; - [Header("Chat Settings")] - [Tooltip("Giới hạn số ký tự tối đa cho mỗi tin nhắn chat")] - public int maxChatLength = 256; + private const int MAX_HISTORY = 10; - // ===== Constants from C++ source ===== - private const string INDENTATION = " "; - private const int MAX_ITEM_WANTED = 10; - private const int MAX_MONSTER_WANTED = 3; - private const int MAX_PLAYER_WANTED = MAX_MONSTER_WANTED; + private struct ChatMsg + { + public ChatChannel channel; + public float time; + public string msg; + public int pack; + public int slot; + } + + private List m_vecHistory = new(); + private int m_nCurHistory = 0; + + private float m_dwTickFarCry = 0; + private float m_dwTickFarCry2 = 0; private void Start() { inputField.onSubmit.AddListener(OnSubmit); - - // Giới hạn ký tự trực tiếp trên InputField - if (inputField.characterLimit <= 0 || inputField.characterLimit > maxChatLength) - inputField.characterLimit = maxChatLength; } - private void OnDestroy() - { - inputField.onSubmit.RemoveListener(OnSubmit); - } - - // ===== Khi nhấn Enter ===== private void OnSubmit(string text) { if (string.IsNullOrWhiteSpace(text)) return; - HandleUserInput(text); + OnCommand_speak(text); } - // ===== Hàm xử lý input (gửi server) ===== - private void HandleUserInput(string text) + // ===================================================== + // PORT C++: CDlgChat::OnCommand_speak + // ===================================================== + + private void OnCommand_speak(string text) { - // Áp dụng giới hạn ký tự - text = TruncateText(text, maxChatLength); + string strText = text.Trim(); - // Lọc từ cấm (bad words filter) - CECUIManager.Instance?.FilterBadWords(ref text); + if (strText.Length <= 0) + { + ChangeFocus(); + return; + } - UnityGameSession.SendChatData( - (byte)ChatChannel.GP_CHAT_LOCAL, - text, - 0, - 0 - ); + int nPack = -1; + int nSlot = -1; - // reset input - inputField.text = ""; - inputField.ActivateInputField(); // focus lại để tiếp tục gõ - } + FilterBadWords(ref strText); - public void Send(string text) - { - if (string.IsNullOrWhiteSpace(text)) + if (HandleDebugCommand(strText)) return; - HandleUserInput(text); + float now = Time.time; + + if (!CheckFarCryRequirement(strText, now)) + return; + + if (!CheckSuperFarCryRequirement(strText, now)) + return; + + if (!CheckSpamProtection(strText, now)) + return; + + ParseAndSendMessage(strText, nPack, nSlot); + + SaveHistory(strText, nPack, nSlot, now); + + ChangeFocus(); } - // ================================================================= - // SECTION: OnCommand_Chat - Chia sẻ trạng thái nhiệm vụ lên chat - // Converted from CDlgTaskTrace::OnCommand_Chat (DlgTaskTrace.cpp) - // ================================================================= + // ===================================================== + // DEBUG COMMAND + // ===================================================== - /// - /// Builds a chat message describing task progress and sends it to the appropriate channel. - /// Gọi hàm này khi người chơi bấm nút "Chia sẻ nhiệm vụ" trên giao diện Task Trace. - /// - /// ID của nhiệm vụ cần chia sẻ. - public void OnCommand_Chat(uint idTask) + private bool HandleDebugCommand(string text) { - // TODO: Replace with actual EC_Game Unity Wrapper when available - // CECHostPlayer pHost = EC_Game.GetGameRun()?.GetHostPlayer(); - // CECTaskInterface pTask = pHost.GetTaskInterface(); - // ATaskTempl pTemp = pTask.GetTaskTemplMan().GetTaskTemplByID(idTask); - - // --- MOCK DATA FOR COMPILATION --- - bool bActiveTask = true; - StringBuilder sb = new StringBuilder(); - - // string strName = pTemp.GetName(); - string strName = $"[Task_{idTask}]"; - strName = DeleteColorStr(strName); - sb.Append(strName); - - /* - // --- Lấy thông tin trạng thái task --- - Task_State_info tsi = new Task_State_info(); - pTask.GetTaskStateInfo(idTask, ref tsi, bActiveTask); - - // --- NPC trao thưởng (nếu có thể hoàn thành task) --- - int nANPC = (int)pTemp.GetAwardNPC(); - if (nANPC > 0 && pTask.CanFinishTask(idTask)) - ... - */ - - // --- MOCK LOGIC TO APPEND PROGRESS --- - sb.Append(INDENTATION); - sb.AppendFormat(GetStringFromTable(248), GetItemName(123), 2, 10); // Mock item progress - - // --- Cắt bớt trailing '\r' --- - string strText = sb.ToString().TrimEnd('\r'); - - // --- Áp dụng giới hạn ký tự --- - strText = TruncateText(strText, maxChatLength); - - // --- Xác định kênh chat: Team nếu có đội, Local nếu không --- - byte channel = (byte)ChatChannel.GP_CHAT_LOCAL; - - // --- Gửi lên server --- - UnityGameSession.SendChatData(channel, strText, 0, 0); - - Debug.Log($"[ChatInputHandler] OnCommand_Chat: Sent mock task {idTask} to channel {channel}, length={strText.Length}"); - } - - // ================================================================= - // SECTION: Helper methods - // ================================================================= - - /// - /// Cắt chuỗi nếu vượt quá giới hạn ký tự, thêm dấu "..." ở cuối. - /// - private static string TruncateText(string text, int maxLength) - { - if (string.IsNullOrEmpty(text) || maxLength <= 0) - return text; - - if (text.Length <= maxLength) - return text; - - // Trừ 3 ký tự để chèn "..." - if (maxLength > 3) - return text.Substring(0, maxLength - 3) + "..."; - else - return text.Substring(0, maxLength); - } - - /// - /// Xóa các mã màu "^xxxxxx" ra khỏi chuỗi. - /// Tương đương CDlgTaskTrace::DeleteColorStr trong C++. - /// - private static string DeleteColorStr(string str) - { - if (string.IsNullOrEmpty(str)) - return str; - - StringBuilder result = new StringBuilder(str.Length); - for (int i = 0; i < str.Length; i++) + if (text == "##debug") { - if (str[i] == '^' && i + 6 < str.Length) + Debug.Log("Toggle debug console"); + return true; + } + + return false; + } + + // ===================================================== + // FARCRY CHECK (!@) + // ===================================================== + + private bool CheckFarCryRequirement(string text, float now) + { + if (text.StartsWith("!@")) + { + int itemNum = GetPlayerItemCount(12979) + GetPlayerItemCount(36092); + + if (itemNum < 1 || GetPlayerLevel() < 5) { - // Kiểm tra 6 ký tự tiếp theo có phải hex không - bool isColorCode = true; - for (int j = 1; j <= 6; j++) - { - char c = str[i + j]; - if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) - { - isColorCode = false; - break; - } - } - - if (isColorCode) - { - i += 6; // bỏ qua 6 ký tự hex - continue; - } + AddChatMessage(CECUIManager.Instance.GameUI.GetStringFromTable(731), ChatChannel.GP_CHAT_MISC); + return false; + } + + if (now - m_dwTickFarCry <= 1f) + { + AddChatMessage(CECUIManager.Instance.GameUI.GetStringFromTable(730), ChatChannel.GP_CHAT_MISC); + return false; } - result.Append(str[i]); } - return result.ToString(); + + return true; } - /// - /// Format thời gian cho task (tương đương CDlgTask::FormatTime trong C++). - /// - private static string FormatTime(int nSec, string format, int totalTime) + // ===================================================== + // SUPER FARCRY (!#) + // ===================================================== + + private bool CheckSuperFarCryRequirement(string text, float now) { - int hours = nSec / 3600; - int minutes = (nSec % 3600) / 60; - int seconds = nSec % 60; - - string timeStr; - if (hours > 0) - timeStr = $"{hours:D2}:{minutes:D2}:{seconds:D2}"; - else - timeStr = $"{minutes:D2}:{seconds:D2}"; - - if (!string.IsNullOrEmpty(format)) + if (text.StartsWith("!#")) { - try { return string.Format(format, timeStr); } - catch { return timeStr; } + int itemNum = GetPlayerItemCount(27728) + GetPlayerItemCount(27729); + + if (itemNum < 1) + { + AddChatMessage(CECUIManager.Instance.GameUI.GetStringFromTable(8531), ChatChannel.GP_CHAT_MISC); + return false; + } + + if (now - m_dwTickFarCry2 <= 1f) + { + AddChatMessage(CECUIManager.Instance.GameUI.GetStringFromTable(8530), ChatChannel.GP_CHAT_MISC); + return false; + } } - return timeStr; + + return true; } - /// - /// Lấy string từ bảng String Table (StringFromTable). - /// Tương đương pUIMan->GetStringFromTable(id) trong C++. - /// - private static string GetStringFromTable(int id) + // ===================================================== + // SPAM PROTECTION + // ===================================================== + + private bool CheckSpamProtection(string text, float now) { - // Sử dụng hệ thống StringTable có sẵn trong project - var gameUI = CECUIManager.Instance?.GetInGameUIMan(); - string result = gameUI?.GetStringFromTable(id); - return result ?? $"[STR_{id}]"; + if (m_vecHistory.Count == 0) + return true; + + var last = m_vecHistory[m_vecHistory.Count - 1]; + + if (now - last.time <= 1f) + { + AddChatMessage(CECUIManager.Instance.GameUI.GetStringFromTable(272), ChatChannel.GP_CHAT_MISC); + return false; + } + + foreach (var cm in m_vecHistory) + { + if (cm.channel != ChatChannel.GP_CHAT_WHISPER && + cm.msg == text && + now - cm.time <= 6f) + { + AddChatMessage(CECUIManager.Instance.GameUI.GetStringFromTable(273), ChatChannel.GP_CHAT_MISC); + return false; + } + } + + return true; } - /// - /// Lấy tên NPC theo ID từ ElementDataMan. - /// - private static string GetNPCName(int npcId) + // ===================================================== + // PARSE MESSAGE PREFIX + // ===================================================== + + private void ParseAndSendMessage(string text, int nPack, int nSlot) { - // TODO: Kết nối với ElementDataMan của bạn khi đã port xong - // var pDataMan = EC_Game.GetElementDataMan(); - // return pDataMan?.GetNPCName(npcId); - return $"NPC_{npcId}"; + ChatChannel channel; + string pszMsg; + + if (text.StartsWith("!!")) + { + channel = ChatChannel.GP_CHAT_TEAM; + pszMsg = text.Substring(2); + } + else if (text.StartsWith("!~")) + { + channel = ChatChannel.GP_CHAT_FACTION; + pszMsg = text.Substring(2); + } + else if (text.StartsWith("!@")) + { + channel = ChatChannel.GP_CHAT_FARCRY; + pszMsg = text.Substring(2); + } + else if (text.StartsWith("!#")) + { + channel = ChatChannel.GP_CHAT_SUPERFARCRY; + pszMsg = text.Substring(2); + } + else if (text.StartsWith("$")) + { + channel = ChatChannel.GP_CHAT_TRADE; + pszMsg = text.Substring(1); + } + else if (text.StartsWith("/")) + { + HandleWhisper(text, nPack, nSlot); + return; + } + else + { + channel = ChatChannel.GP_CHAT_LOCAL; + pszMsg = text; + } + + SendChat(channel, pszMsg, nPack, nSlot); } - /// - /// Lấy tên Monster theo ID từ ElementDataMan. - /// - private static string GetMonsterName(int monsterId) + // ===================================================== + // WHISPER + // ===================================================== + + private void HandleWhisper(string text, int nPack, int nSlot) { - // TODO: Kết nối với ElementDataMan của bạn khi đã port xong - // var pDataMan = EC_Game.GetElementDataMan(); - // return pDataMan?.GetMonsterName(monsterId); - return $"Monster_{monsterId}"; + string cmd = text.Substring(1); + + int spaceIndex = cmd.IndexOf(' '); + + if (spaceIndex <= 0) + { + AddChatMessage(CECUIManager.Instance.GameUI.GetStringFromTable(234), ChatChannel.GP_CHAT_MISC); + return; + } + + string player = cmd.Substring(0, spaceIndex); + string msg = cmd.Substring(spaceIndex + 1); + + SendPrivateChat(player, msg, nPack, nSlot); } - /// - /// Lấy tên Item theo ID từ ElementDataMan. - /// - private static string GetItemName(int itemId) + // ===================================================== + // SEND CHAT + // ===================================================== + + private void SendChat(ChatChannel channel, string msg, int pack, int slot) { - // TODO: Kết nối với ElementDataMan của bạn khi đã port xong - // var pDataMan = EC_Game.GetElementDataMan(); - // return pDataMan?.GetItemName(itemId); - return $"Item_{itemId}"; + UnityGameSession.SendChatData((byte)channel, msg, pack, slot); } + + private void SendPrivateChat(string target, string msg, int pack, int slot) + { + Debug.Log($"Whisper to {target}: {msg}"); + } + + // ===================================================== + // HISTORY + // ===================================================== + + private void SaveHistory(string msg, int pack, int slot, float now) + { + if (m_vecHistory.Count >= MAX_HISTORY) + m_vecHistory.RemoveAt(0); + + m_vecHistory.Add(new ChatMsg + { + channel = ChatChannel.GP_CHAT_LOCAL, + msg = msg, + pack = pack, + slot = slot, + time = now + }); + + m_nCurHistory = m_vecHistory.Count; + } + + // ===================================================== + // UTILITIES + // ===================================================== + + private void FilterBadWords(ref string text) + { + CECUIManager.Instance.FilterBadWords(ref text); + } + + private void AddChatMessage(string msg, ChatChannel channel, int idPlayer = -1, string pszPlayer = "", byte byFlag = 0) + { + string strModified = msg; + + // 1. Filter bad words + CECUIManager.Instance.FilterBadWords(ref strModified); + + if (string.IsNullOrEmpty(strModified)) + return; + + // 2. Blacklist check (Placeholder for porting) + if (IsPlayerBlacklisted(idPlayer)) + return; + + // 3. Flag handling (Friend chat routing) + if (byFlag == 1) // CHANNEL_FRIEND equivalent + { + // AddFriendMessage(strModified, idPlayer, pszPlayer, ...); + // return; + } + + // 4. Formatting with Rich Text + string colorHex = GetChannelColorHex(channel); + string prefix = GetChannelPrefix(channel); + string sender = string.IsNullOrEmpty(pszPlayer) ? "" : $"{pszPlayer}: "; + + string finalMsg = $"{prefix}{sender}{strModified}"; + + // 5. Publish event + EventBus.Publish(new GameSession.ChatMessageEvent(finalMsg, (byte)channel)); + + Debug.Log(finalMsg); + } + + private string GetChannelColorHex(ChatChannel channel) + { + // Simplified mapping based on common PW colors + return channel switch + { + ChatChannel.GP_CHAT_TEAM => "00FFFF", // Cyan + ChatChannel.GP_CHAT_FACTION => "00FF00", // Green + ChatChannel.GP_CHAT_FARCRY => "FFFF00", // Yellow + ChatChannel.GP_CHAT_WHISPER => "FF00FF", // Magenta + _ => "FFFFFF" // White + }; + } + + private string GetChannelPrefix(ChatChannel channel) + { + return channel switch + { + ChatChannel.GP_CHAT_TEAM => "[Team] ", + ChatChannel.GP_CHAT_FACTION => "[Faction] ", + ChatChannel.GP_CHAT_FARCRY => "[World] ", + ChatChannel.GP_CHAT_WHISPER => "[Whisper] ", + _ => "" + }; + } + + private bool IsPlayerBlacklisted(int idPlayer) => false; // Placeholder + + private void ChangeFocus() + { + inputField.text = ""; + inputField.ActivateInputField(); + } + + private int GetPlayerItemCount(int id) => 0; + private int GetPlayerLevel() => 10; } } \ No newline at end of file