Files
Papay-Shooter/debug/character_picker_check.gd
2026-08-09 01:31:44 -04:00

134 lines
5.1 KiB
GDScript

extends SceneTree
## Does character selection work as part of every loadout?
##
## The character picker used to be a separate global screen. It now belongs to
## each loadout, so this checks the full-screen editor, saved character fields,
## selected-item summary, and animated preview together.
##
## 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
menu.visible = true
menu._show_loadouts()
await process_frame
_check(menu.loadout_editor.visible, "Loadout editor shows")
_check(not menu.main_vbox.visible, "Main pause list hides behind it")
_check(menu.loadout_editor.anchor_right == 1.0 and menu.loadout_editor.anchor_bottom == 1.0,
"Loadout editor is anchored full-screen")
_check(menu.loadout_list_vbox.get_child_count() == 5, "All five loadouts are shown")
_check(menu.skin_opt != null and menu.skin_opt.item_count > 0,
"Character selector is inside the loadout editor")
var skin_mgr = root.get_node("SkinManager")
var loadout_mgr = root.get_node("LoadoutManager")
for loadout_index in loadout_mgr.loadouts.size():
var loadout: Dictionary = loadout_mgr.loadouts[loadout_index]
_check(loadout.has("skin"), "'%s' stores its own character" % loadout.get("name", "Loadout"))
menu._edit_loadout(loadout_index)
_check(menu._selected_metadata(menu.skin_opt) == str(loadout.get("skin", "default")),
"'%s' opens with its saved character selected" % loadout.get("name", "Loadout"))
_check(menu.loadout_summary.text.contains(menu._weapon_name(str(loadout.get("primary_1", "none")))),
"'%s' summary displays its selected items" % loadout.get("name", "Loadout"))
var count: int = menu.skin_opt.item_count
_check(count > 0, "Roster is not empty (%d entries)" % count)
var seen_glb := 0
for i in count:
var id: String = menu.skin_opt.get_item_metadata(i)
menu.skin_opt.select(i)
menu._on_skin_option_selected(i)
await process_frame
await process_frame
_check(menu.character_desc.text != "", "'%s' has a description line" % id)
_check(menu.loadout_summary.text.contains(skin_mgr.get_skin(id).skin_name),
"'%s' appears in the selected-item summary" % 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 > 0, "shipping GLB character previews were built (%d)" % seen_glb)
menu._show_main_menu()
await process_frame
_check(not menu.loadout_editor.visible, "Back closes the loadout editor")
_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=== LOADOUT CHARACTER SELECTOR ===\nFailures: %d" % _fails)
quit(1 if _fails > 0 else 0)