extends SceneTree ## Does the escape menu's character picker actually work? ## ## It is built entirely in code, in an autoload, over a paused tree — three ## things that each hide their own class of mistake and none of which a compile ## check catches. So: open it, walk every entry, and assert that each one ## selects, describes itself, and builds a real model with a real skeleton. ## ## godot --headless --path . -s res://debug/character_picker_check.gd var _fails: int = 0 func _init() -> void: root.call_deferred("add_child", Node.new()) # let autoloads finish _ready await process_frame await process_frame var menu = root.get_node_or_null("PauseMenu") _check(menu != null, "PauseMenu autoload exists") if menu == null: _done() return _check(menu.character_btn != null, "Character button exists on the pause menu") _check(menu.character_list != null, "Character list exists") _check(menu.character_editor != null, "Character screen exists") menu._show_character() await process_frame _check(menu.character_editor.visible, "Character screen shows") _check(not menu.main_vbox.visible, "Main pause list hides behind it") # Autoload singletons are not resolvable as identifiers from a `-s` SceneTree # script — it is compiled before they register — so reach it by path. var skin_mgr = root.get_node("SkinManager") var count: int = menu.character_list.item_count _check(count > 0, "Roster is not empty (%d entries)" % count) var seen_glb := 0 for i in count: var id: String = menu.character_list.get_item_metadata(i) menu._on_character_selected(i) await process_frame await process_frame _check(menu.character_desc.text != "", "'%s' has a description line" % id) var skin = skin_mgr.get_skin(id) var expects_model: bool = skin.model_path != "" \ and ResourceLoader.exists(skin.model_path) if not expects_model: # A colour-tint skin has no GLB. The preview must be EMPTY, not the # previously selected character left standing there. _check(menu._preview_model == null, "'%s' is a colour skin and clears the preview" % id) continue seen_glb += 1 var model = menu._preview_model _check(model != null, "'%s' builds a preview model" % id) if model == null: continue _check(model.loaded, "'%s' preview finished loading" % id) _check(model.skeleton != null, "'%s' preview has a skeleton" % id) _check(model.animation_player != null, "'%s' preview has animations" % id) _check(model.surface_table() != null and not model.surface_table().is_empty(), "'%s' preview knows its surface classes" % id) var body: Array = model.surfaces_of(SkinSurfaces.BODY) _check(not body.is_empty(), "'%s' preview reports body surfaces" % id) # A preview that is not ANIMATING is a preview of the bind pose, which # is the one pose the character will never be in during play. The clip # NAME is not evidence of that — it is a variable this class sets on # itself, and it reads "Idle" just as happily when the animation tree # is not ticking at all. So watch the skeleton move. _check(model.current_clip_debug() == "Idle", "'%s' preview selected Idle (got '%s')" % [id, model.current_clip_debug()]) _check(await _pose_moves(model), "'%s' preview skeleton is actually animating" % id) _check(seen_glb >= 6, "every shipping GLB skin previewed (%d)" % seen_glb) # Back out, and make sure the turntable stops costing frames. menu._show_main_menu() await process_frame _check(not menu.character_editor.visible, "Back returns to the pause list") _check(menu.main_vbox.visible, "Pause list is showing again") _done() ## Does the skeleton's pose change over a handful of frames? ## ## Sampled from INSIDE the modifier pass would be better, but the question here ## is only "is anything driving this at all", and for that the animated pose is ## the right thing to read: if the AnimationTree is not ticking, every bone ## holds still and this returns false. func _pose_moves(model) -> bool: var skel: Skeleton3D = model.skeleton if skel == null or skel.get_bone_count() == 0: return false var before: Array = [] for b in skel.get_bone_count(): before.append(skel.get_bone_pose_rotation(b)) for _i in 12: await process_frame for b in skel.get_bone_count(): if not skel.get_bone_pose_rotation(b).is_equal_approx(before[b]): return true return false func _check(ok: bool, what: String) -> void: if ok: print(" OK: %s" % what) else: print(" FAIL: %s" % what) _fails += 1 func _done() -> void: print("\n=== CHARACTER PICKER ===\nFailures: %d" % _fails) quit(1 if _fails > 0 else 0)