extends SceneTree ## Loads every menu-selectable level through an active single-player match, ## waits for its player/viewmodel to initialize, then advances. This is the ## release gate for scene-load, shader, and teardown errors. ## The map list is DISCOVERED, not written down. ## ## It used to be a hardcoded array, and a newly added level (Sakura Crossing) ## loaded, shipped and never once went through this gate — the run still said ## "PASS: all 5 levels" while there were six. A release gate that has to be ## edited by hand every time it gains something to test is a gate that will be ## wrong exactly when it matters. ## ## `scenes/maps/*/map_meta.cfg` is already the definition of "menu-selectable", ## since that is what the main menu and map_preview_capture both scan. const MAPS_DIR := "res://scenes/maps/" var MAPS: Array[String] = [] func _discover_maps() -> Array[String]: var out: Array[String] = [] var dir := DirAccess.open(MAPS_DIR) if dir == null: return out dir.list_dir_begin() var folder := dir.get_next() while folder != "": if dir.current_is_dir() and not folder.begins_with("."): var cfg := ConfigFile.new() if cfg.load(MAPS_DIR + folder + "/map_meta.cfg") == OK: var scene_path: String = cfg.get_value("map", "scene_path", "") if scene_path != "" and ResourceLoader.exists(scene_path): out.append(scene_path) folder = dir.get_next() out.sort() return out var _mode := "boot" var _frames := 0 var _map_index := -1 var _failures: Array[String] = [] var _loaded_maps := 0 var _skin_override := "" func _initialize() -> void: var args := OS.get_cmdline_user_args() if not args.is_empty(): _skin_override = args[0] MAPS = _discover_maps() print("MAP_SMOKE discovered %d levels: %s" % [MAPS.size(), ", ".join(MAPS)]) change_scene_to_file("res://ui/main_menu/main_menu.tscn") func _process(_delta: float) -> bool: _frames += 1 if _mode == "boot": if _frames == 45: if not _skin_override.is_empty(): var skin_manager = root.get_node_or_null("SkinManager") if skin_manager and skin_manager.skins.has(_skin_override): # Test-only selection: direct assignment avoids overwriting # the player's saved preference in user://. skin_manager.active_skin_id = _skin_override print("MAP_SMOKE skin override: ", _skin_override) else: _fail("unknown skin override: " + _skin_override) var network = root.get_node_or_null("NetworkManager") if network and network.has_method("start_singleplayer_match"): network.start_singleplayer_match(GameMode.DEATHMATCH) _load_next() return false if _mode == "map": if _frames == 150: var players := root.find_children("*", "CharacterBody3D", true, false) var local_player = null for player in players: if player.has_method("get_visual_model") \ and player.is_multiplayer_authority(): local_player = player break if local_player == null: _fail("missing local player: " + MAPS[_map_index]) else: var managers: Array[Node] = local_player.find_children( "WeaponManager", "WeaponManager", true, false) if managers.is_empty(): _fail("missing WeaponManager: " + MAPS[_map_index]) else: print("MAP_SMOKE loaded ", MAPS[_map_index]) _loaded_maps += 1 _audit_null_materials() elif _frames >= 180: _load_next() return false if _mode == "drain": if _frames >= 180: if _failures.is_empty() and _loaded_maps == MAPS.size(): print("MAP_SMOKE PASS: all %d levels loaded cleanly" % MAPS.size()) quit(0) else: for failure in _failures: push_error("MAP_SMOKE FAIL: " + failure) quit(1) return true return false return false func _load_next() -> void: _map_index += 1 _frames = 0 if _map_index >= MAPS.size(): _mode = "drain" unload_current_scene() return _mode = "map" var error := change_scene_to_file(MAPS[_map_index]) if error != OK: _fail("could not load %s (error %d)" % [MAPS[_map_index], error]) func _fail(message: String) -> void: _failures.append(message) printerr("MAP_SMOKE ", message) func _audit_null_materials() -> void: var null_surfaces: Array[String] = [] for candidate in root.find_children("*", "MeshInstance3D", true, false): var mesh_instance := candidate as MeshInstance3D if mesh_instance.mesh == null: continue for surface in mesh_instance.mesh.get_surface_count(): if mesh_instance.get_active_material(surface) == null: null_surfaces.append( "%s[%d]" % [mesh_instance.get_path(), surface]) if null_surfaces.is_empty(): print("MAP_SMOKE material audit: 0 null surfaces") else: _fail("%d null mesh materials in %s: %s" % [ null_surfaces.size(), MAPS[_map_index], ", ".join(null_surfaces.slice(0, 12)), ])