From c3bc869457a40defdf89eb2d389cd3470a677122 Mon Sep 17 00:00:00 2001 From: CuongNV <> Date: Thu, 9 Apr 2026 14:52:37 +0700 Subject: [PATCH] add new chat mini --- .../Scripts/Network/CSNetwork/GameSession.cs | 20 +++-- .../Scripts/UI/GamePlay/EC_GameUIMan.cs | 54 ++++++++++--- .../prefab_MiniChatContentsText.prefab | 21 +++++ .../MiniChat/prefab_MiniChatUI.prefab | 8 +- .../ChatSystem/prefab_ChatSystemUI.prefab | 76 +++++++++---------- 5 files changed, 122 insertions(+), 57 deletions(-) diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs index c00cb9b24d..1ebfc44484 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs @@ -2083,11 +2083,16 @@ namespace CSNetwork pGameUI.AddChatMessage(formatted, (ChatChannel)p.Channel, p.Roleid, strSrcName, 0, p.Emotion, null, strMsg); - // Bubble chat on head - CECPlayer pSrcPlayer = EC_Game.GetGameRun().GetWorld().GetPlayerMan().GetPlayer(p.Roleid); - if (pSrcPlayer != null) + // Kênh không thuộc showsAboveHead trong AddChatMessage 时 vẫn cần bong bóng — English: extra bubble for channels AddChatMessage does not publish. + if (!CECGameUIMan.ChannelShowsChatBubbleAboveHead((ChatChannel)p.Channel)) { - EventBus.PublishChannel(p.Roleid, new EventChatMessageOnTopPlayer(p.Roleid, strMsg)); + CECPlayer pSrcPlayer = EC_Game.GetGameRun().GetWorld().GetPlayerMan().GetPlayer(p.Roleid); + if (pSrcPlayer != null) + { + string bubbleTmp = pGameUI.ConvertWireBodyForHeadBubble( + strMsg, (ChatChannel)p.Channel, p.Roleid, p.Emotion); + EventBus.PublishChannel(p.Roleid, new EventChatMessageOnTopPlayer(p.Roleid, bubbleTmp)); + } } return true; @@ -2303,11 +2308,14 @@ namespace CSNetwork p.Channel, p.Emotion, null, strMsg); } - // Set player's last said words for head bubble + // 私聊头顶气泡:AddChatMessage 对 GP_CHAT_WHISPER 不发布 EventChatMessageOnTopPlayer,在此单独发 TMP 正文。 + // Whisper head bubble: AddChatMessage does not publish EventChatMessageOnTopPlayer for whisper — publish TMP body here. CECPlayer pSrcPlayer = EC_Game.GetGameRun().GetWorld().GetPlayerMan().GetPlayer(p.Srcroleid); if (pSrcPlayer != null) { - EventBus.PublishChannel(p.Srcroleid, new EventChatMessageOnTopPlayer(p.Srcroleid, strMsg)); + string bubbleTmp = pGameUI.ConvertWireBodyForHeadBubble( + strMsg, ChatChannel.GP_CHAT_WHISPER, p.Srcroleid, p.Emotion); + EventBus.PublishChannel(p.Srcroleid, new EventChatMessageOnTopPlayer(p.Srcroleid, bubbleTmp)); } } diff --git a/Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs b/Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs index 052a57fd99..a48a0ab231 100644 --- a/Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs +++ b/Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs @@ -58,6 +58,45 @@ namespace BrewMonster.UI return _chatEmotionPipeline.ConvertInlineItemsToTmp(f); } + /// + /// 仅正文 wire(无 printf 包装)→ 头顶气泡 TMP — 与 AddChatMessage 内 FilterBadWords + 内联转 TMP 一致。 + /// Wire body only (no printf wrapper) → head-bubble TMP — same as AddChatMessage FilterBadWords + inline to TMP. + /// + public string ConvertWireBodyForHeadBubble(string wireBody, ChatChannel cChannel, int idPlayer, int cEmotion) + { + if (string.IsNullOrEmpty(wireBody)) + return wireBody; + string s = _chatEmotionPipeline.ApplyChannelEmotionFilter(wireBody, cEmotion); + if (string.IsNullOrEmpty(s)) + return s; + bool isPlayerChannel = cChannel == ChatChannel.GP_CHAT_LOCAL + || cChannel == ChatChannel.GP_CHAT_FARCRY + || cChannel == ChatChannel.GP_CHAT_TEAM + || cChannel == ChatChannel.GP_CHAT_FACTION + || cChannel == ChatChannel.GP_CHAT_WHISPER + || cChannel == ChatChannel.GP_CHAT_TRADE + || cChannel == ChatChannel.GP_CHAT_SUPERFARCRY + || cChannel == ChatChannel.GP_CHAT_BATTLE + || cChannel == ChatChannel.GP_CHAT_COUNTRY; + if (isPlayerChannel && idPlayer > 0) + CECUIManager.Instance.FilterBadWords(ref s); + return _chatEmotionPipeline.ConvertInlineItemsToTmp(s); + } + + /// + /// Kênh có bong bóng trên đầu trong AddChatMessage — dùng để tránh double-publish với PROTOCOL_WORLDCHAT. + /// Channels that get head bubble from AddChatMessage — avoids double-publish with PROTOCOL_WORLDCHAT. + /// + public static bool ChannelShowsChatBubbleAboveHead(ChatChannel cChannel) + { + return cChannel == ChatChannel.GP_CHAT_LOCAL + || cChannel == ChatChannel.GP_CHAT_FARCRY + || cChannel == ChatChannel.GP_CHAT_TEAM + || cChannel == ChatChannel.GP_CHAT_SUPERFARCRY + || cChannel == ChatChannel.GP_CHAT_BATTLE + || cChannel == ChatChannel.GP_CHAT_COUNTRY; + } + // Layout/settings flags for GetUserLayout (saved to server) // 布局/设置标志,用于 GetUserLayout(保存到服务器) private bool m_bAutoReply; @@ -613,18 +652,15 @@ namespace BrewMonster.UI // C++: AddChatMessage also handles head bubble via pPlayer->SetLastSaidWords // Unity equivalent: Publish EventChatMessageOnTopPlayer - bool showsAboveHead = cChannel == ChatChannel.GP_CHAT_LOCAL - || cChannel == ChatChannel.GP_CHAT_FARCRY - || cChannel == ChatChannel.GP_CHAT_TEAM - || cChannel == ChatChannel.GP_CHAT_SUPERFARCRY - || cChannel == ChatChannel.GP_CHAT_BATTLE - || cChannel == ChatChannel.GP_CHAT_COUNTRY; + bool showsAboveHead = ChannelShowsChatBubbleAboveHead(cChannel); if (showsAboveHead && idPlayer > 0) { - // C++: pPlayer->SetLastSaidWords(strTemp, ...) — strTemp là nội dung chat thuần, - // KHÔNG bao gồm tên người chơi. Dùng pszMsgOrigion thay vì pszMsg. - string bubbleText = !string.IsNullOrEmpty(pszMsgOrigion) ? pszMsgOrigion : pszMsg; + // C++: pPlayer->SetLastSaidWords(strTemp, ...) — chỉ thân tin (không tên); bản wire pszMsgOrigion phải → TMP giống khung chat. + // C++: SetLastSaidWords — body only (no name); wire pszMsgOrigion must become TMP like chat panel. + string bubbleText = !string.IsNullOrEmpty(pszMsgOrigion) + ? ConvertWireBodyForHeadBubble(pszMsgOrigion, cChannel, idPlayer, cEmotion) + : pszMsg; EventBus.PublishChannel(idPlayer, new EventChatMessageOnTopPlayer(idPlayer, bubbleText)); } diff --git a/Assets/Prefabs/ChatSystem/MiniChat/prefab_MiniChatContentsText.prefab b/Assets/Prefabs/ChatSystem/MiniChat/prefab_MiniChatContentsText.prefab index bd9320e3a1..575746405b 100644 --- a/Assets/Prefabs/ChatSystem/MiniChat/prefab_MiniChatContentsText.prefab +++ b/Assets/Prefabs/ChatSystem/MiniChat/prefab_MiniChatContentsText.prefab @@ -87,6 +87,7 @@ GameObject: - component: {fileID: 7228077960814023056} - component: {fileID: 5305392080666511277} - component: {fileID: 7604324431011831295} + - component: {fileID: 8883197050699466089} m_Layer: 5 m_Name: Text (TMP) m_TagString: Untagged @@ -236,6 +237,26 @@ MonoBehaviour: m_EditorClassIdentifier: m_HorizontalFit: 0 m_VerticalFit: 2 +--- !u!114 &8883197050699466089 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6240941777052618231} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreLayout: 0 + m_MinWidth: -1 + m_MinHeight: -1 + m_PreferredWidth: 291.7 + m_PreferredHeight: -1 + m_FlexibleWidth: -1 + m_FlexibleHeight: -1 + m_LayoutPriority: 1 --- !u!1 &6627717456258223658 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Prefabs/ChatSystem/MiniChat/prefab_MiniChatUI.prefab b/Assets/Prefabs/ChatSystem/MiniChat/prefab_MiniChatUI.prefab index 756a14d2a9..3d21d9065f 100644 --- a/Assets/Prefabs/ChatSystem/MiniChat/prefab_MiniChatUI.prefab +++ b/Assets/Prefabs/ChatSystem/MiniChat/prefab_MiniChatUI.prefab @@ -368,10 +368,10 @@ RectTransform: - {fileID: 4845475585563704660} m_Father: {fileID: 3676046446549609595} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0} - m_AnchorMax: {x: 0.5, y: 0} - m_AnchoredPosition: {x: -219, y: 141} - m_SizeDelta: {x: 638, y: 227} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: -175.64499, y: 151.8} + m_SizeDelta: {x: -1148.71, y: 227} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &5093099212117339368 CanvasRenderer: diff --git a/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab b/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab index 64a435f09e..dd4e1b0a55 100644 --- a/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab +++ b/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab @@ -664,9 +664,9 @@ RectTransform: m_Children: [] m_Father: {fileID: 5189750139888259924} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 538.9, y: -37.5} m_SizeDelta: {x: 65, y: 65} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &3313632616987073809 @@ -1208,10 +1208,10 @@ RectTransform: - {fileID: 739609527060018247} m_Father: {fileID: 5751167674555460373} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 0, y: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 67, y: -213.5} + m_SizeDelta: {x: 134, y: 51} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &38225373003582705 CanvasRenderer: @@ -1404,10 +1404,10 @@ RectTransform: - {fileID: 610143082050100740} m_Father: {fileID: 5751167674555460373} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 0, y: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 67, y: -45.5} + m_SizeDelta: {x: 134, y: 51} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &7779409169763482013 CanvasRenderer: @@ -1525,9 +1525,9 @@ RectTransform: - {fileID: 4952957933435979972} m_Father: {fileID: 5189750139888259924} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 293.2, y: -37.5} m_SizeDelta: {x: 400, y: 60} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &20947868772470573 @@ -1686,7 +1686,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!224 &1473246120053017968 RectTransform: m_ObjectHideFlags: 0 @@ -1780,9 +1780,9 @@ RectTransform: - {fileID: 5804952772034601221} m_Father: {fileID: 5189750139888259924} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 637.6, y: -37.5} m_SizeDelta: {x: 106, y: 62} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &7641285342810036890 @@ -2248,10 +2248,10 @@ RectTransform: - {fileID: 6015197618098464652} m_Father: {fileID: 5751167674555460373} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 0, y: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 67, y: -157.5} + m_SizeDelta: {x: 134, y: 51} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &3744810705978608696 CanvasRenderer: @@ -2620,7 +2620,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!224 &2739455247741079987 RectTransform: m_ObjectHideFlags: 0 @@ -3086,9 +3086,9 @@ RectTransform: - {fileID: 1242095235490096923} m_Father: {fileID: 5189750139888259924} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 40, y: -37.5} m_SizeDelta: {x: 80, y: 60} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &2491747450976635330 @@ -3227,10 +3227,10 @@ RectTransform: - {fileID: 5053277402852973834} m_Father: {fileID: 5751167674555460373} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 0, y: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 67, y: -101.5} + m_SizeDelta: {x: 134, y: 51} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &78804016905584931 CanvasRenderer: @@ -3801,10 +3801,10 @@ RectTransform: - {fileID: 1012966277253133821} m_Father: {fileID: 5751167674555460373} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 0, y: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 67, y: -269.5} + m_SizeDelta: {x: 134, y: 51} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &7711523176654121571 CanvasRenderer: @@ -4834,10 +4834,10 @@ RectTransform: - {fileID: 7795282280205679886} m_Father: {fileID: 5751167674555460373} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 0, y: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 67, y: -325.5} + m_SizeDelta: {x: 134, y: 51} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &519132139347130033 CanvasRenderer: