Files
test/Assets/Scripts/ChatInputHandler.cs
T
2026-03-24 10:58:19 +07:00

494 lines
16 KiB
C#

using System;
using UnityEngine;
using TMPro;
using CSNetwork.GPDataType;
using System.Collections.Generic;
using BrewMonster.Network;
using CSNetwork;
namespace BrewMonster.Scripts.ChatUI
{
public class ChatInputHandler : MonoBehaviour
{
public TMP_InputField inputField;
public ChatSystemSO chatSystem;
[Serializable]
public struct ChannelButtonMapping
{
public ChatChannel channel;
public UnityEngine.UI.Button button;
}
public List<ChannelButtonMapping> channelButtons = new();
public ChatPanelUI chatPanelUI; // Reference to ChatPanelUI to relay channel changes
private const int MAX_HISTORY = 10;
private ChatChannel m_currentChannel = ChatChannel.GP_CHAT_LOCAL;
private struct ChatMsg
{
public ChatChannel channel;
public float time;
public string msg;
public int pack;
public int slot;
}
private List<ChatMsg> 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);
foreach (var mapping in channelButtons)
{
if (mapping.button != null)
{
ChatChannel ch = mapping.channel; // Capture variable for closure
mapping.button.onClick.AddListener(() => OnCommand_speakmode(ch));
}
}
// Select the first button as default if available
if (channelButtons.Count > 0 && channelButtons[0].button != null)
{
OnCommand_speakmode(channelButtons[0].channel);
}
// [Mobile Fix] Giữ cho chữ được hiển thị trực tiếp trên UI của game thay vì bị
// đẩy vào một thanh ngang phụ (Native OS Input) bật lên cùng bàn phím.
if (inputField != null)
{
inputField.shouldHideMobileInput = true;
}
}
// =====================================================
// PORT C++: CDlgChat::OnCommand_speakmode
// Sets the chat input prefix based on the selected channel from SO.
// =====================================================
private void OnCommand_speakmode(ChatChannel channel)
{
if (chatSystem == null) return;
// Update button visual states using sprites from ChatSystemSO
foreach (var mapping in channelButtons)
{
if (mapping.button != null && mapping.button.image != null)
{
if (mapping.channel == channel)
mapping.button.image.sprite = chatSystem.selectedSprite;
else
mapping.button.image.sprite = chatSystem.unselectedSprite;
}
}
m_currentChannel = channel;
if (chatPanelUI != null)
{
chatPanelUI.SetChannelFilter(channel);
}
var config = chatSystem.channelIcons.Find(c => c.channel == channel);
if (config.prefix != null)
{
string currentText = inputField.text;
currentText = RemoveKnownPrefix(currentText);
inputField.text = config.prefix + currentText;
}
if (channel == ChatChannel.GP_CHAT_SYSTEM)
{
inputField.interactable = false;
inputField.text = ""; // Xóa text nếu chuyển sang kênh hệ thống
}
else
{
inputField.interactable = true;
inputField.ActivateInputField();
inputField.MoveTextEnd(false);
}
}
private string RemoveKnownPrefix(string text)
{
if (string.IsNullOrEmpty(text)) return text;
if (text.StartsWith("!!") || text.StartsWith("!~") ||
text.StartsWith("!@") || text.StartsWith("!#"))
{
return text.Substring(2);
}
else if (text.StartsWith("$"))
{
return text.Substring(1);
}
// Không tự ý remove prefix của Whisper (bắt đầu bằng '/') vì nó đi kèm tên người chơi
return text;
}
private void OnSubmit(string text)
{
if (string.IsNullOrWhiteSpace(text))
return;
OnCommand_speak(text);
}
// =====================================================
// PORT C++: CDlgChat::OnCommand_speak
// =====================================================
private void OnCommand_speak(string text)
{
string strText = text.Trim();
if (strText.Length <= 0)
{
ClearTextInInputField();
//ChangeFocus();
return;
}
int nPack = -1;
int nSlot = -1;
FilterBadWords(ref strText);
if (HandleDebugCommand(strText))
return;
float now = Time.time;
if (!CheckFarCryRequirement(strText, now))
return;
if (!CheckSuperFarCryRequirement(strText, now))
return;
if (!CheckSpamProtection(strText, now))
return;
ChatChannel resolvedChannel = ParseAndSendMessage(strText, nPack, nSlot);
SaveHistory(resolvedChannel, strText, nPack, nSlot, now);
ClearTextInInputField();
//ChangeFocus();
}
// =====================================================
// DEBUG COMMAND
// =====================================================
private bool HandleDebugCommand(string text)
{
if (text == "##debug")
{
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)
{
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;
}
}
return true;
}
// =====================================================
// SUPER FARCRY (!#)
// =====================================================
private bool CheckSuperFarCryRequirement(string text, float now)
{
if (text.StartsWith("!#"))
{
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 true;
}
// =====================================================
// SPAM PROTECTION
// =====================================================
private bool CheckSpamProtection(string text, float now)
{
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;
}
// =====================================================
// PARSE MESSAGE PREFIX
// =====================================================
private ChatChannel ParseAndSendMessage(string text, int nPack, int nSlot)
{
ChatChannel channel = m_currentChannel;
string pszMsg = text;
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 ChatChannel.GP_CHAT_WHISPER;
}
// Không gõ prefix thủ công thì sẽ dùng m_currentChannel đã được gán ở đầu hàm
if (channel == ChatChannel.GP_CHAT_SYSTEM)
{
Debug.Log("[Cuong] ParseAndSendMessage Ngăn người chơi chat ở GP_CHAT_SYSTEM");
return channel;
}
if (channel == ChatChannel.GP_CHAT_WHISPER)
{
// Nếu người chơi đang ở kênh Whisper nhưng (vô tình) xóa mất dấu '/'
// Ta vẫn giả lập thêm '/' vào để parse theo cú pháp "TênNgườiNhận NộiDung"
HandleWhisper("/" + pszMsg, nPack, nSlot);
return channel;
}
SendChat(channel, pszMsg, nPack, nSlot);
return channel;
}
// =====================================================
// WHISPER
// =====================================================
private void HandleWhisper(string text, int nPack, int nSlot)
{
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);
}
// =====================================================
// SEND CHAT
// =====================================================
private void SendChat(ChatChannel channel, string msg, int pack, int slot)
{
UnityGameSession.SendChatData((byte)channel, msg, pack, slot);
}
private void SendPrivateChat(string target, string msg, int pack, int slot)
{
Debug.Log($"Whisper to {target}: {msg}");
// [Port] C++: CDlgChat::OnCommand_speak → privatechat branch
// Gửi tin nhắn mật (whisper) lên server qua GameSession.
UnityGameSession.Instance.GameSession.SendPrivateChatData(target, msg, 0, 0, pack, slot);
// Server không echo whisper lại cho chính mình → hiện local echo
string localEcho = $"<color=#0065FE>[Whisper] → {target}: {msg}</color>";
EventBus.Publish(new GameSession.ChatMessageEvent(localEcho, (byte)ChatChannel.GP_CHAT_WHISPER));
}
// =====================================================
// HISTORY
// =====================================================
private void SaveHistory(ChatChannel channel, string msg, int pack, int slot, float now)
{
if (m_vecHistory.Count >= MAX_HISTORY)
m_vecHistory.RemoveAt(0);
m_vecHistory.Add(new ChatMsg
{
channel = channel,
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;
if (channel is not ChatChannel.GP_CHAT_MISC)
{
// 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 = $"<color=#{colorHex}>{prefix}{sender}{strModified}</color>";
// 5. Publish event
EventBus.Publish(new GameSession.ChatMessageEvent(finalMsg, (byte)channel));
Debug.Log("[Cuong] AddChatMessage" + 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 ClearTextInInputField()
{
inputField.text = "";
}
private void ChangeFocus()
{
inputField.ActivateInputField();
}
private int GetPlayerItemCount(int id) => 0;
private int GetPlayerLevel() => 10;
}
}