100 lines
3.1 KiB
GDScript
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])
|