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: