Files
mtgodot-poc/project/chat_test.gd
T

132 lines
5.1 KiB
GDScript

# chat_test —— P3:聊天窗路由 + 前缀解析 + 气泡 headless 自检。
# godot --headless --path project --script chat_test.gd
extends SceneTree
const ChatUI = preload("res://ui/chat_ui.gd")
const NetWorld = preload("res://net_world.gd")
class FakeClient extends Node:
signal chat(type: int, vid: int, text: String)
# 注:GDScript 不允许 signal 和 func 同名;真 M2Client 是 GDExtension,两者可共存。
# 这里 whisper 用作方法(chat_ui 调 client.whisper(...));入站 whisper 直接调 chat._on_whisper。
signal item_picked_up(vnum: int, count: int, from: String)
signal fishing_event(subheader: int, info: int, dir: int)
# net_world 也连这些:
signal entity_spawned(entity: Dictionary)
signal entity_despawned(vid: int)
signal entity_moved(vid: int)
signal entity_main_set(vid: int)
signal entity_dead(vid: int)
signal vitals_changed(vid: int)
signal damage(vid: int, amount: int, flag: int)
var ents := {1000: {"vid": 1000, "name": "Me"}, 2000: {"vid": 2000, "name": "Bob"}}
var said := []
var whispered := []
func get_entity(vid: int) -> Dictionary: return ents.get(vid, {})
func get_entities() -> Array: return ents.values()
func say(t, s) -> bool: said.append([t, s]); return true
func whisper(to, s) -> bool: whispered.append([to, s]); return true
class FakeProto extends Node:
func item(vnum: int) -> Dictionary:
if vnum == 30001:
return {"vnum": vnum, "type": 12, "locale_name": "Trout"}
return {}
var _fail := 0
func _ck(c: bool, m: String) -> void:
if not c:
_fail += 1
printerr("FAIL: " + m)
func _init() -> void:
_run()
if _fail == 0:
print("PASS: chat_test (routing + prefixes + bubble)")
quit(0)
else:
printerr("%d check(s) failed" % _fail)
quit(1)
func _last(rt: RichTextLabel) -> String:
return rt.get_parsed_text().strip_edges().split("\n")[-1]
func _run() -> void:
var canvas := CanvasLayer.new()
get_root().add_child(canvas)
var fc := FakeClient.new()
get_root().add_child(fc)
var chat: Node = ChatUI.new()
get_root().add_child(chat)
chat.setup(fc, canvas, AssetRoot.path())
var proto := FakeProto.new()
get_root().add_child(proto)
chat.set_proto(proto)
_ck(chat._log.size() == 4, "4 tabs built")
var tAll: RichTextLabel = chat._log[0]
var tWhisper: RichTextLabel = chat._log[1]
var tSys: RichTextLabel = chat._log[2]
var tBattle: RichTextLabel = chat._log[3]
# 普通聊天 -> 全部标签,带名字
fc.chat.emit(0, 2000, "hi all")
_ck(_last(tAll).contains("Bob: hi all"), "talking -> All tab with name")
_ck(chat._buffers[2].size() == 0, "talking not in System tab")
# info -> 系统 + 全部
fc.chat.emit(1, 0, "you leveled up")
_ck(_last(tSys).contains("you leveled up"), "info -> System tab")
_ck(_last(tAll).contains("you leveled up"), "info -> All tab too")
# whisper -> 私聊 + 全部(入站直接调 handler,见 FakeClient 注释)
chat._on_whisper(0, "Alice", "secret")
_ck(_last(tWhisper).contains("Alice: secret"), "whisper -> Whisper tab")
_ck(_last(tAll).contains("Alice: secret"), "whisper -> All tab")
chat._on_whisper(1, "", "server notice")
_ck(_last(tWhisper).contains("[系统]: server notice"), "system whisper -> [系统]")
# 拾取 -> 系统 + 战斗
fc.item_picked_up.emit(27, 3, "")
_ck(_last(tSys).contains("拾取 #27 x3"), "pickup -> System")
_ck(_last(tBattle).contains("拾取 #27 x3"), "pickup -> Battle")
# 钓鱼服务端事件:SUCCESS 只切动画;FISH 按 IsFishing() + item type
# 走 ClientVS22 的 notify/success locale 分支。
fc.fishing_event.emit(0, 1000, 0)
fc.fishing_event.emit(5, 30001, 0)
_ck(_last(tBattle).contains("Trout") and _last(tBattle).contains("hooked"),
"fishing notify fish -> Battle locale")
fc.fishing_event.emit(3, 1000, 0)
fc.fishing_event.emit(5, 30001, 0)
_ck(_last(tSys).contains("You captured Trout!"), "fishing success fish -> System locale")
chat.on_fishing_feedback("FISHING_WRONG_PLACE")
_ck(_last(tSys).contains("You cannot go fishing here."), "wrong place -> locale")
# --- 前缀解析 ---
chat._on_submit("hello world")
_ck(fc.said.size() == 1 and fc.said[0] == [0, "hello world"], "plain -> say(TALKING)")
chat._on_submit("/g guild msg")
_ck(fc.said.size() == 2 and fc.said[1] == [4, "guild msg"], "/g -> say(GUILD)")
chat._on_submit("/s shout!")
_ck(fc.said[2] == [6, "shout!"], "/s -> say(SHOUT)")
chat._on_submit("/w Bob psst hey")
_ck(fc.whispered.size() == 1 and fc.whispered[0] == ["Bob", "psst hey"], "/w Bob ... -> whisper('Bob','psst hey')")
_ck(_last(tWhisper).contains("→ Bob: psst hey"), "/w -> local echo in Whisper tab")
# --- 气泡 ---
var nw: Node = NetWorld.new()
get_root().add_child(nw)
var mount := Node3D.new()
get_root().add_child(mount)
nw.setup(fc, mount)
fc.entity_spawned.emit({"vid": 2000, "name": "Bob", "pos": Vector3.ZERO, "func": 0, "angle_deg": 0.0})
await process_frame
var got_main := [""]
nw.main_bubble.connect(func(t): got_main[0] = t)
nw._bubble(2000, "hi there")
var bob: Node3D = nw.node_for(2000)
_ck(bob != null and bob.get_node_or_null("bubble") != null, "entity 2000 got a bubble node")
nw.set_local_vid(1000)
nw._bubble(1000, "my line")
_ck(got_main[0] == "my line", "local vid bubble -> main_bubble signal")