Files
mtgodot-poc/project/whisper_chat_system.gd
T
shenandshen 66d217b313 feat(client): 完成40250客户端核心功能1:1对齐与桥梁高度采样修复
- 桥梁与静态物体高度采样修复:
  - 严格对齐 40250 CMapOutdoor::GetHeight 与 CAttributeInstance::GetHeight
  - 解析 .mdatr 中的 AttributeHeight 网格,使用 is_in_triangle_2d 准确计算桥面多边形平面方程
  - sample_height 查询邻近区块并返回 fMAX(fObjectHeight, fTerrainHeight),彻底解决走上桥面穿透掉入水底/河床的问题
  - 新增 test_bridge_height_parity.gd 自动化对拍测试
- 40250 怪物击杀经验动效:
  - 1:1 实现 FLY_EXP(0) / FLY_HP / FLY_SP 粒子轨迹与爆炸吸附
- 40250 客户端全系统功能对齐(Batches 1-31):
  - 包含公会、交易、骑乘、变身、钓鱼、采矿、商城、信件、结婚、地牢等 134 套对拍系统与自动化回归测试
- 文档沉淀:
  - 新增 docs/CLIENT-PARITY-AUDIT-AND-FIX-GUIDE.md 客户端对拍缺陷发现与修复工程指南
2026-09-19 08:51:25 -07:00

171 lines
5.1 KiB
GDScript

# whisper_chat_system.gd —— 40250 独立多标签私聊与任务栏闪烁通知系统 1:1
# 严格对照:
# Client/Eternexus/root/uiwhisper.py (WhisperDialog, WhisperButton)
# ClientVS22/source/UserInterface/PythonChat.cpp (RenderWhisper, WhisperMode)
# ClientVS22/source/UserInterface/PythonPlayer.cpp (ToggleIgnoreTarget, IsIgnoreTarget)
extends RefCounted
const COLOR_NORMAL := "#FFFFFF"
const COLOR_GM := "#FFE700" # 官方 GM 专属亮金黄色
const COLOR_SYSTEM := "#00FFFF"
enum DialogState {
VISIBLE, # 展开在前台
MINIMIZED, # 最小化到底部任务栏
CLOSED # 完全关闭
}
class WhisperMessage:
var sender: String
var content: String
var timestamp: int
var is_gm: bool = false
func _init(p_sender: String, p_content: String, p_time: int, p_is_gm: bool = false) -> void:
sender = p_sender
content = p_content
timestamp = p_time
is_gm = p_is_gm
class WhisperConversation:
var target_name: String
var is_gamemaster: bool = false
var state: int = DialogState.VISIBLE
var is_blinking: bool = false # 任务栏闪烁标识
var unread_count: int = 0
var history: Array = [] # Array of WhisperMessage
func _init(p_target: String, p_is_gm: bool = false) -> void:
target_name = p_target
is_gamemaster = p_is_gm
state = DialogState.VISIBLE
is_blinking = false
unread_count = 0
history = []
var main_player_name: String = "Hero"
var conversations: Dictionary = {} # target_name -> WhisperConversation
var ignored_targets: Dictionary = {} # target_name -> bool
var current_time: int = 1000
func _init(player_name: String = "Hero") -> void:
main_player_name = player_name
conversations.clear()
ignored_targets.clear()
current_time = 1000
# 1. 打开或激活与某人的私聊窗口 (uiwhisper.py: OpenWhisperDialog)
func open_conversation(target_name: String) -> WhisperConversation:
if target_name.is_empty():
return null
var conv: WhisperConversation = null
if conversations.has(target_name):
conv = conversations[target_name]
conv.state = DialogState.VISIBLE
conv.is_blinking = false
conv.unread_count = 0
else:
var is_gm: bool = target_name.begins_with("[GM]")
conv = WhisperConversation.new(target_name, is_gm)
conversations[target_name] = conv
return conv
# 2. 最小化私聊窗口到任务栏 (uiwhisper.py: Minimize)
func minimize_conversation(target_name: String) -> bool:
if not conversations.has(target_name):
return false
conversations[target_name].state = DialogState.MINIMIZED
return true
# 3. 关闭私聊窗口 (uiwhisper.py: Close)
func close_conversation(target_name: String) -> bool:
if not conversations.has(target_name):
return false
conversations[target_name].state = DialogState.CLOSED
return true
# 4. 收到私聊消息 (1:1 PythonChat::AppendWhisper)
func receive_whisper(sender_name: String, text: String, is_gm_override: bool = false) -> Dictionary:
var res := {
"delivered": false,
"blocked_by_ignore": false,
"triggered_blink": false,
"conversation": null,
"error_code": "OK"
}
# 检查黑名单免打扰 (ToggleIgnoreTarget / IsIgnoreTarget)
if is_ignored(sender_name):
res["blocked_by_ignore"] = true
res["error_code"] = "BLOCKED_BY_IGNORE"
return res
var is_gm: bool = is_gm_override or sender_name.begins_with("[GM]")
# 获取或创建会话
var conv: WhisperConversation = null
if conversations.has(sender_name):
conv = conversations[sender_name]
else:
conv = WhisperConversation.new(sender_name, is_gm)
conv.state = DialogState.MINIMIZED # 新收到的私聊若未预先开启则最小化并在任务栏闪烁
conversations[sender_name] = conv
var msg = WhisperMessage.new(sender_name, text, current_time, is_gm)
conv.history.append(msg)
# 若处于最小化或后台,激活任务栏闪烁与未读计数
if conv.state == DialogState.MINIMIZED or conv.state == DialogState.CLOSED:
conv.is_blinking = true
conv.unread_count += 1
res["triggered_blink"] = true
res["delivered"] = true
res["conversation"] = conv
return res
# 5. 发送私聊消息
func send_whisper(target_name: String, text: String) -> Dictionary:
var res := { "success": false, "error_code": "OK", "message": "" }
if target_name.is_empty():
res["error_code"] = "EMPTY_TARGET"
return res
if text.strip_edges().is_empty():
res["error_code"] = "EMPTY_MESSAGE"
return res
var conv = open_conversation(target_name)
var msg = WhisperMessage.new(main_player_name, text, current_time, false)
conv.history.append(msg)
res["success"] = true
return res
# 6. 拉黑/解除拉黑目标 (1:1 player.ToggleIgnoreTarget)
func toggle_ignore_target(target_name: String) -> bool:
if target_name.is_empty():
return false
if ignored_targets.has(target_name):
ignored_targets.erase(target_name)
return false # 现在未拉黑
else:
ignored_targets[target_name] = true
return true # 现在已拉黑
func is_ignored(target_name: String) -> bool:
return ignored_targets.get(target_name, false)
# 7. 获取当前所有在任务栏闪烁的会话列表
func get_blinking_conversations() -> Array:
var list: Array = []
for k in conversations:
var c: WhisperConversation = conversations[k]
if c.is_blinking:
list.append(c)
return list