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

100 lines
3.1 KiB
GDScript

extends SceneTree
## Photograph every character in the loadout selector, one shot each.
##
## The picker builds a real SkinnedPlayerModel per character, so this is the
## cheapest full-roster visual check there is: it catches a rig whose idle pose
## came out wrong, a skin that lost its textures, cloth that never settles, and
## a character standing the wrong way round — none of which any assertion in
## debug/character_picker_check.gd can see.
##
## godot --path . -s res://debug/roster_capture.gd -- <output_dir>
var _out_dir := "."
var _frames := 0
var _index := -1
var _menu: Node = null
## The character currently posing. Tracked explicitly rather than recomputed
## from the list index at shutter time — _next() skips the colour-tint skins,
## so the index when the photo is taken is not the index it was selected at,
## and every file came out labelled with the wrong character.
var _posing: String = ""
## Frames to let a freshly-built character load, blend into idle and let its
## hair and skirt stop swinging before the shutter opens.
const SETTLE := 90
func _initialize() -> void:
var args := OS.get_cmdline_user_args()
if args.size() > 0:
_out_dir = args[0]
if not _out_dir.is_absolute_path():
_out_dir = ProjectSettings.globalize_path(_out_dir)
var mkdir_error := DirAccess.make_dir_recursive_absolute(_out_dir)
if mkdir_error != OK:
printerr("roster_capture: could not create '%s' (error %d)" % [
_out_dir, mkdir_error])
quit(mkdir_error)
return
change_scene_to_file("res://ui/main_menu/main_menu.tscn")
func _process(_delta: float) -> bool:
_frames += 1
if _frames == 40:
var nm = root.get_node_or_null("NetworkManager")
if nm and nm.has_method("start_singleplayer_match"):
nm.start_singleplayer_match(GameMode.DEATHMATCH)
change_scene_to_file("res://scenes/maps/test_level/test_level.tscn")
return false
if _frames < 150:
return false
if _menu == null:
_menu = root.get_node_or_null("PauseMenu")
if _menu == null:
print("no PauseMenu autoload")
quit(1)
return true
_menu.visible = true
_menu._show_loadouts()
_next()
return false
if (_frames - 150) % SETTLE == 0:
_shoot()
_next()
return false
func _next() -> void:
_index += 1
while _index < _menu.skin_opt.item_count:
var id: String = _menu.skin_opt.get_item_metadata(_index)
# Colour-tint skins have no model; there is nothing to photograph.
_menu.skin_opt.select(_index)
_menu._on_skin_option_selected(_index)
if _menu._preview_model != null:
_posing = id
return
_index += 1
_posing = ""
quit()
func _shoot() -> void:
if _posing == "":
return
var id := _posing
var img := root.get_texture().get_image()
# Crop to the preview panel — the rest of the frame is the level behind it.
var w := img.get_width()
var h := img.get_height()
img = img.get_region(Rect2i(int(w * 0.735), int(h * 0.145),
int(w * 0.245), int(h * 0.82)))
var path := "%s/roster_%s.png" % [_out_dir, id]
var save_error := img.save_png(path)
if save_error == OK:
print("wrote %s" % path)
else:
printerr("roster_capture: could not save '%s' (error %d)" % [
path, save_error])