494 lines
16 KiB
C#
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;
|
|
}
|
|
}
|