- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧 - inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域 - 包含其他累积的功能开发和测试文件
59 lines
2.3 KiB
GDScript
59 lines
2.3 KiB
GDScript
extends SceneTree
|
|
|
|
const PlayerView = preload("res://ui/player_view.gd")
|
|
const EquipModel = preload("res://ui/equip_model.gd")
|
|
var failures: Array[String] = []
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("run")
|
|
|
|
func check(ok: bool, message: String) -> void:
|
|
if not ok:
|
|
failures.append(message)
|
|
printerr(message)
|
|
|
|
func run() -> void:
|
|
for race in 8:
|
|
var pv := PlayerView.new()
|
|
root.add_child(pv)
|
|
if not pv.build(AssetRoot.path(), race):
|
|
check(false, "race %d failed to build" % race)
|
|
pv.queue_free()
|
|
continue
|
|
pv.anim.set_process(false)
|
|
pv.anim.set("blend_time", 0.0)
|
|
var equip := EquipModel.new()
|
|
pv.add_child(equip)
|
|
equip.setup_remote(null, func() -> Node: return pv, AssetRoot.path(), race, [0, 0, 0, 0])
|
|
# MSM is an independent asset authority for the equipped body's gender.
|
|
var body_dir := String(pv.model.get("gr2_path")).get_base_dir().to_lower()
|
|
check(body_dir == pv.motion_dir.get_base_dir().to_lower(),
|
|
"race %d: MSM body and animation directories differ" % race)
|
|
check(String(pv.model.get("hair_gr2")).to_lower().begins_with(body_dir + "/"),
|
|
"race %d: hair is from a different resource family" % race)
|
|
for motion in ["wait", "walk", "run", "attack", "damage", "dead"]:
|
|
pv.set_anim_state(motion)
|
|
# RaceData can select weighted variants; assert the registered basename,
|
|
# not one random sample from the vector.
|
|
var variants: Array = [motion]
|
|
if motion == "wait":
|
|
variants = ["wait", "wait_1", "wait_2"]
|
|
elif motion == "attack":
|
|
variants = ["attack", "attack_1"]
|
|
elif motion == "damage":
|
|
variants = ["damage", "damage_1"]
|
|
elif motion == "dead":
|
|
variants = ["dead"]
|
|
check(String(pv.anim.get("anim_path")).get_file().get_basename() in variants,
|
|
"race %d: missing %s motion" % [race, motion])
|
|
for fraction in [0.0, 0.5, 1.0]:
|
|
pv.anim.set("time", float(pv.anim.call("get_duration")) * fraction)
|
|
for mi in pv.model.find_children("*", "MeshInstance3D", true, false):
|
|
var bounds: AABB = mi.get_aabb()
|
|
check(bounds.position.is_finite() and bounds.size.is_finite() and bounds.size.length() > 0,
|
|
"race %d: invalid bounds in %s" % [race, motion])
|
|
pv.queue_free()
|
|
await process_frame
|
|
print("race_motion_assembly_test: failures=", failures.size(), " (8 races, 6 motions, 3 samples)")
|
|
quit(0 if failures.is_empty() else 1)
|