# mob_view_test —— 怪 / NPC 真模型(MobView)headless 自检。 # godot --headless --path project --script mob_view_test.gd # 需要 Metin2Model 扩展 + Metin2 资产(AssetRoot.path());缺就跳过对应检查。 extends SceneTree const MobView = preload("res://ui/mob_view.gd") 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: mob_view_test") quit(0) else: printerr("%d check(s) failed" % _fail) quit(1) func _run() -> void: if not ClassDB.class_exists("Metin2Model") or not ClassDB.class_exists("Metin2Proto"): print(" (skip: 扩展未注册)") return var assets := AssetRoot.path() if not DirAccess.dir_exists_absolute(assets.path_join("Monster")): print(" (skip: 无 Monster 资产)") return var proto: Object = ClassDB.instantiate("Metin2Proto") get_root().add_child(proto) var mp := assets.path_join("locale/locale/en/mob_proto") if not FileAccess.file_exists(mp) or not proto.call("load_mob_proto", mp): print(" (skip: 无 mob_proto)") return # race 102 = Wolf -> assets/*/ymir work/monster/wolf/ var mv: Node3D = MobView.new() get_root().add_child(mv) var ok: bool = mv.build(assets, proto, 102) _ck(ok, "MobView.build(102 = Wolf) 成功") if not ok: return _ck(mv._dir.get_file() == "wolf", "解到 wolf 目录(%s)" % mv._dir) _ck(mv.model != null and mv.model.get_class() == "Metin2Model", "Metin2Model 建好") _ck(mv._motions.size() >= 3, "motlist.txt 解出 %d 个动作" % mv._motions.size()) _ck(mv._motions.has("WAIT"), "有 WAIT 动作") # 状态切换:run -> RUN / attack -> NORMAL_ATTACK(存在才断言路径变化) mv.set_anim_state("run") var s1: String = mv._state mv.set_anim_state("attack") _ck(mv._state == "attack" and s1 == "run", "set_anim_state 切换记录") # 未知 race -> build 失败(返回 null 用占位) var mv2: Node3D = MobView.new() get_root().add_child(mv2) _ck(not mv2.build(assets, proto, 999999), "未知 race -> build 失败") # race 20009 = Old Man(NPC)——若有目录就该建成 var mv3: Node3D = MobView.new() get_root().add_child(mv3) var npc_ok: bool = mv3.build(assets, proto, 20009) print(" NPC 20009 (Old Man) build = %s (%s)" % [npc_ok, mv3._dir])