extends Node3D ## The reusable game shell: player, camera, HUD and the PS1 filter — everything that ## outlives a single level. On load it instances the active level's module (geometry, ## spawners, lighting, colliders) into LevelRoot. Switching levels frees the whole ## previous module first, so no old mesh or collider can survive into the next fight. @onready var _level_root: Node3D = $LevelRoot func _ready() -> void: Run.ensure_run() load_active_level() ## Tear down the current level module and build the run's active one. Freeing the whole ## subtree (not hiding it) is the point: a level's colliders and spawners cease to exist ## before the next level's are created. func load_active_level() -> void: for child: Node in _level_root.get_children(): _level_root.remove_child(child) child.queue_free() var level := Run.active_level() if level == null or level.level_scene == null: push_warning("game_shell: active level has no level_scene") return _level_root.add_child(level.level_scene.instantiate())