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 -- 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])