diff --git a/config_resources/default_settings.tres b/config_resources/default_settings.tres index 11549f33b..65150c3f8 100644 --- a/config_resources/default_settings.tres +++ b/config_resources/default_settings.tres @@ -2,7 +2,7 @@ [ext_resource type="PackedScene" uid="uid://dxep4xm1sl8im" path="res://scenes/key_mapping_menu.tscn" id="1_qho2n"] [ext_resource type="PackedScene" uid="uid://cl4m4q2eurcn0" path="res://scenes/options_menu.tscn" id="2_b4w4u"] -[ext_resource type="Script" uid="uid://cgtk0ximoo5ty" path="res://scripts/game_settings_resource.gd" id="3_qb8m4"] +[ext_resource type="Script" uid="uid://cgtk0ximoo5ty" path="res://scripts/resources/game_settings_resource.gd" id="3_qb8m4"] [resource] script = ExtResource("3_qb8m4") diff --git a/files/docs/Development_Guide.md b/files/docs/Development_Guide.md index 82821a3ab..d50676c20 100644 --- a/files/docs/Development_Guide.md +++ b/files/docs/Development_Guide.md @@ -81,22 +81,25 @@ else: - Tip: Use Tween for fades (e.g., modulate.a from 0 to 1). -### Working with Game Settings (Observer Pattern) - -The project now uses an Observer Pattern for configuration. -When adding a new setting: - -1. Define the Property: Add the property to - `scripts/game_settings_resource.gd` with - a proper setter that emits the `setting_changed` signal. -2. Update Globals: Add the new property to the `_save_settings()` and - `_load_settings()` functions in globals.gd to ensure persistence. -3. UI Integration: Connect your UI elements to the `Globals.settings.setting_changed` - signal. - - Crucial: Always disconnect this signal in `_on_tree_exited()` to prevent - memory leaks and stale observers. -4. No Manual Saves: Do not call save functions directly from the UI; changing the - resource value is sufficient to trigger a save. +### Adding a New Game Setting +To introduce a new global setting (e.g., `difficulty`), follow this pattern: + +1. **Define the Property**: Add the variable to + `res://scripts/resources/game_settings_resource.gd` with an `@export` + annotation so it can be edited in the Godot inspector. Update the custom + `_to_string` method. This resource holds the active state and emits a + `setting_changed` signal upon modification. +2. **Persist the Value**: Persistence is exclusively managed by `globals.gd`. + Update `_save_settings()` and `_load_settings()` in `globals.gd` + to ensure the new property from `game_settings_resource.gd` is read from + and written to `user://settings.cfg` using `ConfigFile`. + +| Step | File | Action | +|:-------------------|:----------------------------------------------------|:------------------------------------------------------------------------------------------------| +| 1. Definition | `res://scripts/resources/game_settings_resource.gd` | Add `@export var property_name: Type = DefaultValue` and update `_to_string()` | +| 2. Persistence | `globals.gd` | Add to ConfigFile `.set_value()` in `_save_settings()` and `.get_value()` in `_load_settings()` | +| 3. Observer Signal | `globals.gd` | Add the variable name to `_on_setting_changed` to filter log spam or prevent excessive disk I/O | +| 4. UI Binding | `res://scenes/options_menu.tscn` | Create slider/toggle and connect signals to update `Globals.settings.property_name` | #### The technical documentation for the "Working with Game Settings" section @@ -118,15 +121,15 @@ use the following signature: - `new_value`: **Variant** (The newly assigned, clamped value) #### 2. Core Files Reference - -| Component | File Path | Responsibility | -|----------------------------|-------------------------------------------|-----------------------------------------------------------------------------------| -| **Data Source (Subject)** | `res://scripts/game_settings_resource.gd` | Defines properties (difficulty, log level), performs clamping, and emits signals. | -| **Logic Observer** | `res://scripts/globals.gd` | Connects to the resource to trigger centralized logging and `_save_settings()`. | -| **UI Observer (Gameplay)** | `res://scripts/gameplay_settings.gd` | Syncs sliders and labels with the resource state using `set_value_no_signal`. | -| **UI Observer (Advanced)** | `res://scripts/advanced_settings.gd` | Syncs log level dropdowns and handles web-specific JavaScript callbacks. | -| **Persistence Settings** | `res://scripts/settings.gd` | Manages low-level `InputMap` serialization and legacy migration logic. | - + +| Component | File Path | Responsibility | +|-------------------------------------|-----------------------------------------------------|--------------------------------------------------------------------------------------------| +| **Data Source (Subject)** | `res://scripts/resources/game_settings_resource.gd` | Defines properties (difficulty, log level), performs clamping, and emits signals. | +| **Logic Observer** | `res://scripts/globals.gd` | Connects to the resource to trigger centralized logging and `_save_settings()`. | +| **UI Observer (Gameplay)** | `res://scripts/ui/menus/gameplay_settings.gd` | Syncs sliders and labels with the resource state using `set_value_no_signal`. | +| **UI Observer (Advanced)** | `res://scripts/ui/menus/advanced_settings.gd` | Syncs log level dropdowns and handles web-specific JavaScript callbacks. | +| **Persistence Settings** | `res://scripts/settings.gd` | Manages low-level `InputMap` serialization and legacy migration logic. | + #### 3. Connection Example for UI To prevent infinite recursion, UI handlers should always check for equality or use `no_signal` methods when responding to the resource: diff --git a/project.godot b/project.godot index b701295a4..90cfcdd6b 100644 --- a/project.godot +++ b/project.godot @@ -26,9 +26,9 @@ general/default_playback_type.web=0 Globals="*res://scripts/core/globals.gd" Settings="*res://scripts/core/settings.gd" -AudioConstants="*res://scripts/audio_constants.gd" +AudioConstants="*res://scripts/resources/audio_constants.gd" AudioManager="*res://scripts/managers/audio_manager.gd" -AudioWebBridge="*res://scripts/audio_web_bridge.gd" +AudioWebBridge="*res://scripts/system/audio_web_bridge.gd" [debug] diff --git a/scenes/Player.tscn b/scenes/Player.tscn index 88c2f26d6..44eba70fd 100644 --- a/scenes/Player.tscn +++ b/scenes/Player.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=7 format=3 uid="uid://37rarq1yywmc"] -[ext_resource type="Script" uid="uid://bcplrymg5tt06" path="res://scripts/player.gd" id="1_tuyoq"] +[ext_resource type="Script" uid="uid://bcplrymg5tt06" path="res://scripts/entities/player.gd" id="1_tuyoq"] [ext_resource type="PackedScene" uid="uid://bafpwe0qf0vm0" path="res://scenes/weapon.tscn" id="2_fjrip"] [ext_resource type="PackedScene" uid="uid://blc0ioe12jlnk" path="res://scenes/bullet.tscn" id="3_smehm"] [ext_resource type="PackedScene" uid="uid://bh47kclxgeeah" path="res://scenes/rotor_left.tscn" id="4_ur7pv"] diff --git a/scenes/bullet.tscn b/scenes/bullet.tscn index 844120b72..28c87b852 100644 --- a/scenes/bullet.tscn +++ b/scenes/bullet.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=4 format=3 uid="uid://blc0ioe12jlnk"] -[ext_resource type="Script" uid="uid://uyw8e8i6adxn" path="res://scripts/bullet.gd" id="1_mkf8s"] +[ext_resource type="Script" uid="uid://uyw8e8i6adxn" path="res://scripts/entities/bullet.gd" id="1_mkf8s"] [ext_resource type="Texture2D" uid="uid://b7utqr70u1mex" path="res://files/sprite/laser_sprites/01.png" id="2_y25gk"] [sub_resource type="CircleShape2D" id="CircleShape2D_l5glv"] diff --git a/scenes/main_scene.tscn b/scenes/main_scene.tscn index d3831c25f..bdf8c39b2 100644 --- a/scenes/main_scene.tscn +++ b/scenes/main_scene.tscn @@ -76,8 +76,8 @@ [ext_resource type="Texture2D" uid="uid://btyfigdtk3x88" path="res://files/random_decor/crates_4.png" id="68_f3krf"] [ext_resource type="Texture2D" uid="uid://bvxu5x1awjrjv" path="res://files/random_decor/dirt_001.png" id="69_7tyuc"] [ext_resource type="Texture2D" uid="uid://f4hxu68qa4fi" path="res://files/random_decor/dirt_002.png" id="70_isor2"] -[ext_resource type="Script" uid="uid://blu5qujicfa7e" path="res://scripts/hud.gd" id="72_sgkfd"] -[ext_resource type="Script" uid="uid://b5x2ehdthhrla" path="res://scripts/parallax_manager.gd" id="76_qj6t7"] +[ext_resource type="Script" uid="uid://blu5qujicfa7e" path="res://scripts/ui/hud.gd" id="72_sgkfd"] +[ext_resource type="Script" uid="uid://b5x2ehdthhrla" path="res://scripts/managers/parallax_manager.gd" id="76_qj6t7"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_pu3yx"] bg_color = Color(0.2627451, 0.2627451, 0.2627451, 0.5882353) diff --git a/scenes/weapon.tscn b/scenes/weapon.tscn index 1a491d7f6..4246d4896 100644 --- a/scenes/weapon.tscn +++ b/scenes/weapon.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://bafpwe0qf0vm0"] -[ext_resource type="Script" uid="uid://dcr6gx3oaboj8" path="res://scripts/weapon.gd" id="1_xasec"] +[ext_resource type="Script" uid="uid://dcr6gx3oaboj8" path="res://scripts/entities/weapon.gd" id="1_xasec"] [node name="WeaponManager" type="Node2D"] script = ExtResource("1_xasec") diff --git a/scripts/core/game_paths.gd b/scripts/core/game_paths.gd new file mode 100644 index 000000000..625a31f07 --- /dev/null +++ b/scripts/core/game_paths.gd @@ -0,0 +1,52 @@ +## Copyright (C) 2026 Egor Kostan +## SPDX-License-Identifier: GPL-3.0-or-later +## game_paths.gd +## Centralized repository for all hardcoded script and scene paths. +## Use this class to reference paths globally to avoid fragility and improve refactoring. + +class_name GamePaths +extends RefCounted + +# ========================================================= +# SCRIPT PATHS +# ========================================================= + +## Path to the player entity script. +const PLAYER: String = "res://scripts/entities/player.gd" + +## Path to the HUD UI script. +const HUD: String = "res://scripts/ui/hud.gd" + +## Path to the audio web bridge system script. +const AUDIO_WEB_BRIDGE: String = "res://scripts/system/audio_web_bridge.gd" + +## Path to the input remap button component script. +const INPUT_REMAP_BUTTON: String = "res://scripts/ui/components/input_remap_button.gd" + +## Path to the gameplay settings menu script. +const GAMEPLAY_SETTINGS: String = "res://scripts/ui/menus/gameplay_settings.gd" + +## Path to the core settings singleton/script. +const SETTINGS: String = "res://scripts/core/settings.gd" + +# ========================================================= +# SCENE PATHS +# ========================================================= + +## Path to the audio settings menu scene. +const AUDIO_SETTINGS_SCENE: String = "res://scenes/audio_settings.tscn" + +## Path to the main game scene. +const MAIN_SCENE: String = "res://scenes/main_scene.tscn" + +## Path to the key mapping menu scene. +const KEY_MAPPING_SCENE: String = "res://scenes/key_mapping_menu.tscn" + +## Path to the gameplay settings menu scene. +const GAMEPLAY_SETTINGS_SCENE: String = "res://scenes/gameplay_settings.tscn" + +## Path to the pause menu scene. +const PAUSE_MENU_SCENE: String = "res://scenes/pause_menu.tscn" + +## Path to the options menu scene. +const OPTIONS_MENU_SCENE: String = "res://scenes/options_menu.tscn" diff --git a/scripts/core/game_paths.gd.uid b/scripts/core/game_paths.gd.uid new file mode 100644 index 000000000..37b71b0da --- /dev/null +++ b/scripts/core/game_paths.gd.uid @@ -0,0 +1 @@ +uid://cj4el7nxnxm07 diff --git a/scripts/bullet.gd b/scripts/entities/bullet.gd similarity index 100% rename from scripts/bullet.gd rename to scripts/entities/bullet.gd diff --git a/scripts/bullet.gd.uid b/scripts/entities/bullet.gd.uid similarity index 100% rename from scripts/bullet.gd.uid rename to scripts/entities/bullet.gd.uid diff --git a/scripts/player.gd b/scripts/entities/player.gd similarity index 100% rename from scripts/player.gd rename to scripts/entities/player.gd diff --git a/scripts/player.gd.uid b/scripts/entities/player.gd.uid similarity index 100% rename from scripts/player.gd.uid rename to scripts/entities/player.gd.uid diff --git a/scripts/weapon.gd b/scripts/entities/weapon.gd similarity index 100% rename from scripts/weapon.gd rename to scripts/entities/weapon.gd diff --git a/scripts/weapon.gd.uid b/scripts/entities/weapon.gd.uid similarity index 100% rename from scripts/weapon.gd.uid rename to scripts/entities/weapon.gd.uid diff --git a/scripts/parallax_manager.gd b/scripts/managers/parallax_manager.gd similarity index 100% rename from scripts/parallax_manager.gd rename to scripts/managers/parallax_manager.gd diff --git a/scripts/parallax_manager.gd.uid b/scripts/managers/parallax_manager.gd.uid similarity index 100% rename from scripts/parallax_manager.gd.uid rename to scripts/managers/parallax_manager.gd.uid diff --git a/scripts/audio_constants.gd b/scripts/resources/audio_constants.gd similarity index 100% rename from scripts/audio_constants.gd rename to scripts/resources/audio_constants.gd diff --git a/scripts/audio_constants.gd.uid b/scripts/resources/audio_constants.gd.uid similarity index 100% rename from scripts/audio_constants.gd.uid rename to scripts/resources/audio_constants.gd.uid diff --git a/scripts/game_settings_resource.gd b/scripts/resources/game_settings_resource.gd similarity index 100% rename from scripts/game_settings_resource.gd rename to scripts/resources/game_settings_resource.gd diff --git a/scripts/game_settings_resource.gd.uid b/scripts/resources/game_settings_resource.gd.uid similarity index 100% rename from scripts/game_settings_resource.gd.uid rename to scripts/resources/game_settings_resource.gd.uid diff --git a/scripts/JavaScriptBridgeWrapper.gd b/scripts/system/JavaScriptBridgeWrapper.gd similarity index 100% rename from scripts/JavaScriptBridgeWrapper.gd rename to scripts/system/JavaScriptBridgeWrapper.gd diff --git a/scripts/JavaScriptBridgeWrapper.gd.uid b/scripts/system/JavaScriptBridgeWrapper.gd.uid similarity index 100% rename from scripts/JavaScriptBridgeWrapper.gd.uid rename to scripts/system/JavaScriptBridgeWrapper.gd.uid diff --git a/scripts/OSWrapper.gd b/scripts/system/OSWrapper.gd similarity index 100% rename from scripts/OSWrapper.gd rename to scripts/system/OSWrapper.gd diff --git a/scripts/OSWrapper.gd.uid b/scripts/system/OSWrapper.gd.uid similarity index 100% rename from scripts/OSWrapper.gd.uid rename to scripts/system/OSWrapper.gd.uid diff --git a/scripts/audio_web_bridge.gd b/scripts/system/audio_web_bridge.gd similarity index 100% rename from scripts/audio_web_bridge.gd rename to scripts/system/audio_web_bridge.gd diff --git a/scripts/audio_web_bridge.gd.uid b/scripts/system/audio_web_bridge.gd.uid similarity index 100% rename from scripts/audio_web_bridge.gd.uid rename to scripts/system/audio_web_bridge.gd.uid diff --git a/scripts/hud.gd b/scripts/ui/hud.gd similarity index 100% rename from scripts/hud.gd rename to scripts/ui/hud.gd diff --git a/scripts/hud.gd.uid b/scripts/ui/hud.gd.uid similarity index 100% rename from scripts/hud.gd.uid rename to scripts/ui/hud.gd.uid diff --git a/test/gut/gut_test_helper.gd b/test/gut/gut_test_helper.gd index 3466c3f3a..209dd3b7e 100644 --- a/test/gut/gut_test_helper.gd +++ b/test/gut/gut_test_helper.gd @@ -4,7 +4,7 @@ ## Shared helper functions and mock builders for GUT unit tests. extends RefCounted -const PLAYER_SCRIPT_PATH: String = "res://scripts/player.gd" +const PLAYER_SCRIPT_PATH: String = GamePaths.PLAYER ## Helper to safely hard-free a node without causing engine crashes. @@ -60,7 +60,7 @@ static func build_mock_player_scene() -> Node: panel.add_child(stats) # Assign the extracted hud.gd script directly to the mock panel - var hud_script := load("res://scripts/hud.gd") + var hud_script := load(GamePaths.HUD) if hud_script: panel.set_script(hud_script) diff --git a/test/gut/test_audio_reset_button.gd b/test/gut/test_audio_reset_button.gd index e4b82c798..2685c44f7 100644 --- a/test/gut/test_audio_reset_button.gd +++ b/test/gut/test_audio_reset_button.gd @@ -8,7 +8,7 @@ extends "res://addons/gut/test.gd" -var audio_scene: PackedScene = load("res://scenes/audio_settings.tscn") +var audio_scene: PackedScene = load(GamePaths.AUDIO_SETTINGS_SCENE) var audio_instance: Control var test_config_path: String = "user://test_reset.cfg" diff --git a/test/gut/test_audio_web_bridge.gd b/test/gut/test_audio_web_bridge.gd index e0a001433..438a9f44f 100644 --- a/test/gut/test_audio_web_bridge.gd +++ b/test/gut/test_audio_web_bridge.gd @@ -7,7 +7,7 @@ extends "res://addons/gut/test.gd" -const AudioWebBridge = preload("res://scripts/audio_web_bridge.gd") +const AudioWebBridge = preload(GamePaths.AUDIO_WEB_BRIDGE) func before_each() -> void: # Reset AudioManager to a known clean state before each test diff --git a/test/gut/test_blank_key_labels_on_missing_config.gd b/test/gut/test_blank_key_labels_on_missing_config.gd index 6b738c4f8..1a5f7480b 100644 --- a/test/gut/test_blank_key_labels_on_missing_config.gd +++ b/test/gut/test_blank_key_labels_on_missing_config.gd @@ -17,7 +17,7 @@ extends GutTest -const InputRemapButton := preload("res://scripts/input_remap_button.gd") +const InputRemapButton := preload(GamePaths.INPUT_REMAP_BUTTON) const TEST_CONFIG_MISSING_PATH: String = "user://test_blank_missing.cfg" const TEST_CONFIG_PARTIAL_PATH: String = "user://test_blank_partial.cfg" diff --git a/test/gut/test_decor_layer_transformations.gd b/test/gut/test_decor_layer_transformations.gd index ce826de96..172ce9382 100644 --- a/test/gut/test_decor_layer_transformations.gd +++ b/test/gut/test_decor_layer_transformations.gd @@ -15,7 +15,7 @@ var viewport_mock: Vector2 = Vector2(1920, 1080) func before_each() -> void: await get_tree().process_frame - main_scene = preload("res://scenes/main_scene.tscn").instantiate() + main_scene = preload(GamePaths.MAIN_SCENE).instantiate() add_child(main_scene) await get_tree().process_frame diff --git a/test/gut/test_deduplication_on_device_switch.gd b/test/gut/test_deduplication_on_device_switch.gd index ac675d4a4..2c5564142 100644 --- a/test/gut/test_deduplication_on_device_switch.gd +++ b/test/gut/test_deduplication_on_device_switch.gd @@ -8,7 +8,7 @@ extends GutTest -const InputRemapButton: Script = preload("res://scripts/input_remap_button.gd") +const InputRemapButton: Script = preload(GamePaths.INPUT_REMAP_BUTTON) const TEST_ACTION: String = "speed_up" var button: InputRemapButton diff --git a/test/gut/test_deduplication_on_rapid_remap.gd b/test/gut/test_deduplication_on_rapid_remap.gd index 538b39b6d..dcf463dec 100644 --- a/test/gut/test_deduplication_on_rapid_remap.gd +++ b/test/gut/test_deduplication_on_rapid_remap.gd @@ -8,7 +8,7 @@ extends GutTest -const InputRemapButton: Script = preload("res://scripts/input_remap_button.gd") +const InputRemapButton: Script = preload(GamePaths.INPUT_REMAP_BUTTON) const TEST_ACTION: String = "speed_up" var button: InputRemapButton diff --git a/test/gut/test_deduplication_on_remap.gd b/test/gut/test_deduplication_on_remap.gd index 9aea26820..b5631c940 100644 --- a/test/gut/test_deduplication_on_remap.gd +++ b/test/gut/test_deduplication_on_remap.gd @@ -8,7 +8,7 @@ extends GutTest -const InputRemapButton: Script = preload("res://scripts/input_remap_button.gd") +const InputRemapButton: Script = preload(GamePaths.INPUT_REMAP_BUTTON) const TEST_ACTION: String = "speed_up" var button: InputRemapButton diff --git a/test/gut/test_deduplication_on_reset.gd b/test/gut/test_deduplication_on_reset.gd index 7dacc3cc0..d211e1e1a 100644 --- a/test/gut/test_deduplication_on_reset.gd +++ b/test/gut/test_deduplication_on_reset.gd @@ -20,7 +20,7 @@ func before_each() -> void: def_ev.physical_keycode = KEY_W InputMap.action_add_event(TEST_ACTION, def_ev) InputMap.action_add_event(TEST_ACTION, def_ev.duplicate()) # Duplicate - menu = load("res://scenes/key_mapping_menu.tscn").instantiate() + menu = load(GamePaths.KEY_MAPPING_SCENE).instantiate() add_child(menu) menu.keyboard.button_pressed = true # Keyboard mode diff --git a/test/gut/test_fuel_additional_edge_cases.gd b/test/gut/test_fuel_additional_edge_cases.gd index d39c600ae..005ad3823 100644 --- a/test/gut/test_fuel_additional_edge_cases.gd +++ b/test/gut/test_fuel_additional_edge_cases.gd @@ -28,7 +28,7 @@ func test_fuel_consumption_with_scaling() -> void: # NEW: Instantiate the main scene locally and use GUT's add_child_autoqfree(). # This ensures the scene and all its dynamically generated Sprite2D children are safely queued for deletion. - main_scene = load("res://scenes/main_scene.tscn").instantiate() + main_scene = load(GamePaths.MAIN_SCENE).instantiate() add_child_autoqfree(main_scene) player_root = main_scene.get_node("Player") player_root.fuel_timer.stop() diff --git a/test/gut/test_fuel_integration.gd b/test/gut/test_fuel_integration.gd index 8b0df78d5..41662cf03 100644 --- a/test/gut/test_fuel_integration.gd +++ b/test/gut/test_fuel_integration.gd @@ -2,6 +2,7 @@ ## SPDX-License-Identifier: GPL-3.0-or-later ## test_fuel_integration_gut.gd ## GUT integration tests for Fuel System signals and persistence. + extends "res://addons/gut/test.gd" const TEST_CONFIG_PATH: String = "user://test_fuel_persistence.cfg" diff --git a/test/gut/test_game_settings_resource.gd b/test/gut/test_game_settings_resource.gd index 80b5219e7..dcb0d7272 100644 --- a/test/gut/test_game_settings_resource.gd +++ b/test/gut/test_game_settings_resource.gd @@ -10,7 +10,7 @@ extends "res://addons/gut/test.gd" -const GameplaySettings = preload("res://scripts/gameplay_settings.gd") +const GameplaySettings = preload(GamePaths.GAMEPLAY_SETTINGS) var gameplay_menu: Control var _resource: GameSettingsResource @@ -20,7 +20,7 @@ func before_each() -> void: Globals.settings = _resource # Instantiate the menu for initialization tests - gameplay_menu = load("res://scenes/gameplay_settings.tscn").instantiate() + gameplay_menu = load(GamePaths.GAMEPLAY_SETTINGS_SCENE).instantiate() # Inject mock wrapper to avoid real JS/OS calls during unit tests gameplay_menu.os_wrapper = OSWrapper.new() @@ -74,7 +74,7 @@ func test_gs_ready_01_02_ui_initialization_sync() -> void: var test_difficulty: float = 1.7 _resource.difficulty = test_difficulty - var new_menu: Variant = load("res://scenes/gameplay_settings.tscn").instantiate() + var new_menu: Variant = load(GamePaths.GAMEPLAY_SETTINGS_SCENE).instantiate() add_child_autofree(new_menu) await get_tree().process_frame @@ -121,7 +121,7 @@ func test_gs_ready_06_safe_init_non_web() -> void: stub(mock_os, "has_feature").to_return(false) # FIX: Instantiate from the SCENE, not just the script - var menu: Control = load("res://scenes/gameplay_settings.tscn").instantiate() + var menu: Control = load(GamePaths.GAMEPLAY_SETTINGS_SCENE).instantiate() menu.os_wrapper = mock_os add_child_autofree(menu) diff --git a/test/gut/test_gameplay_settings_js.gd b/test/gut/test_gameplay_settings_js.gd index c7a7f5fd5..5769724db 100644 --- a/test/gut/test_gameplay_settings_js.gd +++ b/test/gut/test_gameplay_settings_js.gd @@ -7,13 +7,13 @@ extends "res://addons/gut/test.gd" -const GameplaySettings = preload("res://scripts/gameplay_settings.gd") +const GameplaySettings = preload(GamePaths.GAMEPLAY_SETTINGS) var gameplay_menu: Control func before_each() -> void: # Fresh resource to isolate state Globals.settings = GameSettingsResource.new() - gameplay_menu = load("res://scenes/gameplay_settings.tscn").instantiate() + gameplay_menu = load(GamePaths.GAMEPLAY_SETTINGS_SCENE).instantiate() # Inject mock wrapper to simulate web environment gameplay_menu.os_wrapper = OSWrapper.new() add_child_autofree(gameplay_menu) diff --git a/test/gut/test_gameplay_settings_lifecycle.gd b/test/gut/test_gameplay_settings_lifecycle.gd index aec871436..c2df570c9 100644 --- a/test/gut/test_gameplay_settings_lifecycle.gd +++ b/test/gut/test_gameplay_settings_lifecycle.gd @@ -6,12 +6,12 @@ extends "res://addons/gut/test.gd" -const GameplaySettings = preload("res://scripts/gameplay_settings.gd") +const GameplaySettings = preload(GamePaths.GAMEPLAY_SETTINGS) var gameplay_menu: Control func before_each() -> void: Globals.settings = GameSettingsResource.new() - gameplay_menu = load("res://scenes/gameplay_settings.tscn").instantiate() + gameplay_menu = load(GamePaths.GAMEPLAY_SETTINGS_SCENE).instantiate() gameplay_menu.os_wrapper = OSWrapper.new() add_child_autofree(gameplay_menu) await get_tree().process_frame @@ -56,7 +56,7 @@ func test_gs_life_02_back_button_restoration() -> void: ## GS-LIFE-08 | Web overlay visibility cleanup func test_gs_life_08_web_overlay_cleanup() -> void: - var test_menu: Control = load("res://scenes/gameplay_settings.tscn").instantiate() + var test_menu: Control = load(GamePaths.GAMEPLAY_SETTINGS_SCENE).instantiate() var mock_js_bridge: Variant = double(JavaScriptBridgeWrapper).new() var mock_os: Variant = double(OSWrapper).new() @@ -93,7 +93,7 @@ func test_gs_life_05_null_globals_safety() -> void: ## GS-LIFE-09 | Unexpected removal (unintentional exit) restores previous menu func test_gs_life_09_unexpected_removal_restoration() -> void: # 1. Setup: Create fresh instance but do not parent yet - var test_menu: Control = load("res://scenes/gameplay_settings.tscn").instantiate() + var test_menu: Control = load(GamePaths.GAMEPLAY_SETTINGS_SCENE).instantiate() # 2. Mock a previous menu in the stack var mock_prev: Control = Control.new() diff --git a/test/gut/test_gameplay_settings_ui.gd b/test/gut/test_gameplay_settings_ui.gd index 3e789ef1c..a709ea9ed 100644 --- a/test/gut/test_gameplay_settings_ui.gd +++ b/test/gut/test_gameplay_settings_ui.gd @@ -7,7 +7,7 @@ extends "res://addons/gut/test.gd" -const GameplaySettings = preload("res://scripts/gameplay_settings.gd") +const GameplaySettings = preload(GamePaths.GAMEPLAY_SETTINGS) var gameplay_menu: Control var _resource: GameSettingsResource @@ -16,7 +16,7 @@ func before_each() -> void: _resource = GameSettingsResource.new() Globals.settings = _resource - gameplay_menu = load("res://scenes/gameplay_settings.tscn").instantiate() + gameplay_menu = load(GamePaths.GAMEPLAY_SETTINGS_SCENE).instantiate() # Inject mock wrapper to bypass real web/OS calls gameplay_menu.os_wrapper = OSWrapper.new() diff --git a/test/gut/test_get_pause_binding_label_for_device.gd b/test/gut/test_get_pause_binding_label_for_device.gd index 33aa48cc1..791de58ac 100644 --- a/test/gut/test_get_pause_binding_label_for_device.gd +++ b/test/gut/test_get_pause_binding_label_for_device.gd @@ -7,7 +7,7 @@ extends GutTest var settings: Node func before_each() -> void: - settings = preload("res://scripts/settings.gd").new() + settings = preload(GamePaths.SETTINGS).new() add_child_autofree(settings) # Ensure clean input state diff --git a/test/gut/test_input_remap_button.gd b/test/gut/test_input_remap_button.gd index c8934158d..9a030e6e0 100644 --- a/test/gut/test_input_remap_button.gd +++ b/test/gut/test_input_remap_button.gd @@ -8,7 +8,7 @@ extends "res://addons/gut/test.gd" -const InputRemapButton = preload("res://scripts/input_remap_button.gd") +const InputRemapButton = preload(GamePaths.INPUT_REMAP_BUTTON) var button: InputRemapButton const TEST_ACTION: String = "test_action" diff --git a/test/gut/test_input_remap_button_device_aware.gd b/test/gut/test_input_remap_button_device_aware.gd index 2b246d26a..decaf8a21 100644 --- a/test/gut/test_input_remap_button_device_aware.gd +++ b/test/gut/test_input_remap_button_device_aware.gd @@ -8,7 +8,7 @@ extends "res://addons/gut/test.gd" -const InputRemapButton: Script = preload("res://scripts/input_remap_button.gd") +const InputRemapButton: Script = preload(GamePaths.INPUT_REMAP_BUTTON) var button: InputRemapButton const TEST_ACTION: String = "test_action" diff --git a/test/gut/test_input_remap_ec.gd b/test/gut/test_input_remap_ec.gd index 123cbf4f6..e0cc32155 100644 --- a/test/gut/test_input_remap_ec.gd +++ b/test/gut/test_input_remap_ec.gd @@ -6,7 +6,7 @@ extends "res://addons/gut/test.gd" const TEST_ACTION: String = "test_action" -const InputRemapButton = preload("res://scripts/input_remap_button.gd") +const InputRemapButton = preload(GamePaths.INPUT_REMAP_BUTTON) var original_input_map: Dictionary = {} var button: InputRemapButton diff --git a/test/gut/test_integration_key_mapping.gd b/test/gut/test_integration_key_mapping.gd index 5116ff8fc..6240ba26b 100644 --- a/test/gut/test_integration_key_mapping.gd +++ b/test/gut/test_integration_key_mapping.gd @@ -15,7 +15,7 @@ extends GutTest -const InputRemapButton: Script = preload("res://scripts/input_remap_button.gd") +const InputRemapButton: Script = preload(GamePaths.INPUT_REMAP_BUTTON) const TEST_ACTION: String = "speed_up" # Example action from UI; adjust if needed. const TEST_CONFIG_PATH: String = "user://settings.cfg" const KEY_W_CODE: int = Key.KEY_W # 87, default assumed. @@ -92,7 +92,7 @@ func test_int_01_load_to_ui() -> void: var key_ev: InputEventKey = key_events[0] assert_eq(key_ev.physical_keycode, KEY_Z_CODE, "Loaded keycode should match config") # Instantiate menu and add to tree (UI updates in _ready via update_button_text) - menu = load("res://scenes/key_mapping_menu.tscn").instantiate() + menu = load(GamePaths.KEY_MAPPING_SCENE).instantiate() add_child(menu) # Get specific remap button for test action (keyboard default) var speed_up_btn: InputRemapButton = menu.get_node("Panel/Options/KeyMapContainer/PlayerKeyMap/KeyMappingSpeedUp/SpeedUpInputRemap") @@ -112,7 +112,7 @@ func test_int_02_remap_persist() -> void: gut.p("INT-02: Remap updates InputMap/UI, saves to config; reload restores to disk/UI sync.") # Setup: Default mapping (assume reset sets to "W"); instantiate menu Settings.reset_to_defaults("keyboard") # Ensure defaults loaded - menu = load("res://scenes/key_mapping_menu.tscn").instantiate() + menu = load(GamePaths.KEY_MAPPING_SCENE).instantiate() add_child(menu) var speed_up_btn: InputRemapButton = menu.get_node("Panel/Options/KeyMapContainer/PlayerKeyMap/KeyMappingSpeedUp/SpeedUpInputRemap") assert_eq(speed_up_btn.text, "W", "Should start with default 'W'") @@ -164,7 +164,7 @@ func test_int_03_reset_via_ui() -> void: config.set_value("input", TEST_ACTION, ["key:" + str(KEY_Z_CODE)]) config.save(TEST_CONFIG_PATH) Settings.load_input_mappings() - menu = load("res://scenes/key_mapping_menu.tscn").instantiate() + menu = load(GamePaths.KEY_MAPPING_SCENE).instantiate() add_child(menu) var speed_up_btn: InputRemapButton = menu.get_node("Panel/Options/KeyMapContainer/PlayerKeyMap/KeyMappingSpeedUp/SpeedUpInputRemap") assert_eq(speed_up_btn.text, "Z", "Should start with custom 'Z'") diff --git a/test/gut/test_key_mapping_menu.gd b/test/gut/test_key_mapping_menu.gd index b8194e27f..6ee06a396 100644 --- a/test/gut/test_key_mapping_menu.gd +++ b/test/gut/test_key_mapping_menu.gd @@ -24,7 +24,7 @@ func before_all() -> void: func before_each() -> void: - menu = load("res://scenes/key_mapping_menu.tscn").instantiate() # Adjust path if needed + menu = load(GamePaths.KEY_MAPPING_SCENE).instantiate() # Adjust path if needed add_child(menu) # Get UI references keyboard_btn = menu.get_node("Panel/Options/DeviceTypeContainer/Keyboard") diff --git a/test/gut/test_key_mapping_menu_device_aware.gd b/test/gut/test_key_mapping_menu_device_aware.gd index 633dd9690..99a12cee2 100644 --- a/test/gut/test_key_mapping_menu_device_aware.gd +++ b/test/gut/test_key_mapping_menu_device_aware.gd @@ -75,7 +75,7 @@ func before_each() -> void: if ev_gp: InputMap.action_add_event(action, ev_gp) - menu = load("res://scenes/key_mapping_menu.tscn").instantiate() + menu = load(GamePaths.KEY_MAPPING_SCENE).instantiate() add_child(menu) keyboard_btn = menu.get_node("Panel/Options/DeviceTypeContainer/Keyboard") diff --git a/test/gut/test_main_scene_orphan_nodes.gd b/test/gut/test_main_scene_orphan_nodes.gd index 2c644b6f5..afa2bd848 100644 --- a/test/gut/test_main_scene_orphan_nodes.gd +++ b/test/gut/test_main_scene_orphan_nodes.gd @@ -11,7 +11,7 @@ var viewport_mock: Vector2 = Vector2(1920, 1080) func before_each() -> void: await get_tree().process_frame - main_scene = preload("res://scenes/main_scene.tscn").instantiate() + main_scene = preload(GamePaths.MAIN_SCENE).instantiate() add_child(main_scene) await get_tree().process_frame @@ -84,7 +84,7 @@ func test_scene_reload_lifecycle() -> void: main_scene = null await get_tree().process_frame - var reloaded_scene: MainScene = preload("res://scenes/main_scene.tscn").instantiate() + var reloaded_scene: MainScene = preload(GamePaths.MAIN_SCENE).instantiate() add_child(reloaded_scene) reloaded_scene.setup_bushes_layer(viewport_mock) await get_tree().process_frame diff --git a/test/gut/test_main_scene_parallax_and_performance.gd b/test/gut/test_main_scene_parallax_and_performance.gd index 66f2c6c8f..f79e67903 100644 --- a/test/gut/test_main_scene_parallax_and_performance.gd +++ b/test/gut/test_main_scene_parallax_and_performance.gd @@ -16,7 +16,7 @@ var viewport_mock: Vector2 = Vector2(1920, 1080) func before_each() -> void: await get_tree().process_frame - main_scene = preload("res://scenes/main_scene.tscn").instantiate() + main_scene = preload(GamePaths.MAIN_SCENE).instantiate() add_child(main_scene) await get_tree().process_frame diff --git a/test/gut/test_master_volume_control_and_music.gd b/test/gut/test_master_volume_control_and_music.gd index 2ef2a6a43..cce759aeb 100644 --- a/test/gut/test_master_volume_control_and_music.gd +++ b/test/gut/test_master_volume_control_and_music.gd @@ -8,7 +8,7 @@ extends "res://addons/gut/test.gd" -var audio_scene: PackedScene = load("res://scenes/audio_settings.tscn") +var audio_scene: PackedScene = load(GamePaths.AUDIO_SETTINGS_SCENE) var audio_instance: Control var test_config_path: String = "user://test_music.cfg" @@ -347,10 +347,16 @@ func test_tc_music_14() -> void: assert_true(AudioManager.music_muted) # No unmute -## TC-Music-15 | Unexpected exit (queue_free without back press) | Simulate tree_exited | Previous menu (e.g., options) visible = true; hidden_menus.pop_back(); If web, backPressed restored; Overlays hidden. +## TC-Music-15 | Unexpected exit (queue_free without back press) | Simulate tree_exited | Previous menu (e.g., options) visible = true; hidden_menus.pop_back(); +## If web, backPressed restored; Overlays hidden. ## :rtype: void func test_tc_music_15() -> void: - var prev_menu: Control = Control.new() + # var prev_menu: Control = Control.new() + # FIX: Wrap the newly created Control in GUT's autofree() to prevent it from becoming an orphan memory leak. + # Since this node is only used to simulate a hidden menu and is never added to the scene tree, + # it requires explicit lifecycle management which autofree() handles for us. + var prev_menu: Control = autofree(Control.new()) + prev_menu.visible = false Globals.hidden_menus = [prev_menu] audio_instance.queue_free() diff --git a/test/gut/test_pause_menu.gd b/test/gut/test_pause_menu.gd index de9324a70..bdfedcbea 100644 --- a/test/gut/test_pause_menu.gd +++ b/test/gut/test_pause_menu.gd @@ -11,7 +11,7 @@ extends GutTest -var PauseMenuScene: PackedScene = preload("res://scenes/pause_menu.tscn") +var PauseMenuScene: PackedScene = preload(GamePaths.PAUSE_MENU_SCENE) var pause_menu: CanvasLayer = null var original_paused: bool = false var original_input_map: Dictionary = {} # action: [events] @@ -31,13 +31,13 @@ func before_all() -> void: ## Sets up per-test state. ## Captures/restores paused; instantiates menu; ensures "pause" action. -## Stubs Globals methods to prevent side effects. ## :rtype: void func before_each() -> void: original_paused = get_tree().paused - stub(Globals, 'log_message').to_do_nothing() - stub(Globals, 'load_scene_with_loading').to_do_nothing() - stub(Globals, 'load_options').to_do_nothing() + + # Removed stub() calls here. GUT cannot directly stub Autoload instances, + # and these methods are not invoked by the PM-01 to PM-05 pause/resume flows anyway. + pause_menu = PauseMenuScene.instantiate() get_tree().root.add_child(pause_menu) pause_menu.visible = false diff --git a/test/gut/test_player_lifecycle.gd b/test/gut/test_player_lifecycle.gd index fff8dc3ea..5589f84d0 100644 --- a/test/gut/test_player_lifecycle.gd +++ b/test/gut/test_player_lifecycle.gd @@ -33,7 +33,7 @@ func test_exit_tree_disconnects_signals() -> void: gut.p("Testing: Player _exit_tree properly disconnects global signals.") # 1. Instantiate and add to tree to trigger _ready() and the signal connections - main_scene = load("res://scenes/main_scene.tscn").instantiate() + main_scene = load(GamePaths.MAIN_SCENE).instantiate() add_child(main_scene) player_root = main_scene.get_node("Player") @@ -73,7 +73,7 @@ func test_exit_tree_safe_without_globals() -> void: Globals.settings = null # Instantiate manually without adding to the tree (bypasses _ready) - main_scene = load("res://scenes/main_scene.tscn").instantiate() + main_scene = load(GamePaths.MAIN_SCENE).instantiate() player_root = main_scene.get_node("Player") # Safely call _exit_tree in isolation diff --git a/test/gut/test_preserve_other_sections.gd b/test/gut/test_preserve_other_sections.gd index a97972c6b..2c1697602 100644 --- a/test/gut/test_preserve_other_sections.gd +++ b/test/gut/test_preserve_other_sections.gd @@ -10,7 +10,7 @@ extends "res://addons/gut/test.gd" var test_config_path: String = "user://settings.cfg" var backup_path: String = "user://settings.backup.cfg" -var audio_scene: PackedScene = load("res://scenes/audio_settings.tscn") +var audio_scene: PackedScene = load(GamePaths.AUDIO_SETTINGS_SCENE) var audio_instance: Control @@ -39,9 +39,11 @@ func before_each() -> void: if AudioServer.get_bus_index(AudioConstants.BUS_SFX_ROTORS) == -1: AudioServer.add_bus() AudioServer.set_bus_name(AudioServer.get_bus_count() - 1, AudioConstants.BUS_SFX_ROTORS) - # Reset Globals settings + # Reset Globals via the GameSettingsResource safely to prevent auto-save disk I/O + Globals._is_loading_settings = true Globals.settings.current_log_level = Globals.LogLevel.INFO Globals.settings.difficulty = 1.0 + Globals._is_loading_settings = false ## Per-test cleanup: Free audio_instance safely. @@ -85,7 +87,8 @@ func test_tc_sl_06() -> void: assert_eq(config.get_value("Settings", "difficulty"), 1.5) -## TC-SL-07 | Config with "input", "Settings", and "audio" (old audio values). | Call AudioManager.load_volumes() | Only audio vars updated from "audio" section; "input"/"Settings" ignored by AudioManager; Globals.current_log_level/difficulty unchanged; apply_all_volumes() called. +## TC-SL-07 | Config with "input", "Settings", and "audio" (old audio values). | Call AudioManager.load_volumes() | Only audio vars updated from "audio" section; +## "input"/"Settings" ignored by AudioManager; Globals.current_log_level/difficulty unchanged; apply_all_volumes() called. ## :rtype: void func test_tc_sl_07() -> void: var config: ConfigFile = ConfigFile.new() @@ -95,18 +98,25 @@ func test_tc_sl_07() -> void: config.set_value("audio", "master_volume", 0.4) config.set_value("audio", "master_muted", true) config.save(test_config_path) - # Set Globals to different values + + # Guard the Globals changes so they don't trigger an automatic _save_settings() to disk + Globals._is_loading_settings = true Globals.settings.current_log_level = Globals.LogLevel.DEBUG Globals.settings.difficulty = 2.0 + Globals._is_loading_settings = false + # Load audio only AudioManager.load_volumes() AudioManager.apply_all_volumes() + # Audio updated assert_almost_eq(AudioManager.master_volume, 0.4, 0.01) assert_true(AudioManager.master_muted) - # Globals unchanged (not loaded here) + + # Globals unchanged by AudioManager.load_volumes() (still DEBUG / 2.0 set above) assert_eq(Globals.settings.current_log_level, Globals.LogLevel.DEBUG) assert_eq(Globals.settings.difficulty, 2.0) + # Config unchanged config = ConfigFile.new() config.load(test_config_path) diff --git a/test/gut/test_reset_scenarios.gd b/test/gut/test_reset_scenarios.gd index 0bd565246..573553e18 100644 --- a/test/gut/test_reset_scenarios.gd +++ b/test/gut/test_reset_scenarios.gd @@ -9,7 +9,7 @@ extends "res://addons/gut/test.gd" var test_config_path: String = "user://test_reset.cfg" -var audio_scene: PackedScene = load("res://scenes/audio_settings.tscn") +var audio_scene: PackedScene = load(GamePaths.AUDIO_SETTINGS_SCENE) var audio_instance: Control @@ -37,9 +37,11 @@ func before_each() -> void: if AudioServer.get_bus_index(AudioConstants.BUS_SFX_ROTORS) == -1: AudioServer.add_bus() AudioServer.set_bus_name(AudioServer.get_bus_count() - 1, AudioConstants.BUS_SFX_ROTORS) - # Reset Globals - Globals.current_log_level = Globals.LogLevel.INFO - Globals.difficulty = 1.0 + # Reset Globals via the GameSettingsResource safely + Globals._is_loading_settings = true + Globals.settings.current_log_level = Globals.LogLevel.INFO + Globals.settings.difficulty = 1.0 + Globals._is_loading_settings = false ## Per-test cleanup: Free audio_instance safely. diff --git a/test/gut/test_settings_unbound_scenarios.gd b/test/gut/test_settings_unbound_scenarios.gd index 34dc881ba..e9c696876 100644 --- a/test/gut/test_settings_unbound_scenarios.gd +++ b/test/gut/test_settings_unbound_scenarios.gd @@ -44,7 +44,7 @@ func before_each() -> void: InputMap.action_erase_events(action) else: InputMap.add_action(action) - menu = load("res://scenes/key_mapping_menu.tscn").instantiate() + menu = load(GamePaths.KEY_MAPPING_SCENE).instantiate() add_child(menu) speed_up_btn = menu.get_node("Panel/Options/KeyMapContainer/PlayerKeyMap/KeyMappingSpeedUp/SpeedUpInputRemap") # Default keyboard diff --git a/test/gut/test_sfx_rotor_volume_control.gd b/test/gut/test_sfx_rotor_volume_control.gd index d3d647a19..a52eba533 100644 --- a/test/gut/test_sfx_rotor_volume_control.gd +++ b/test/gut/test_sfx_rotor_volume_control.gd @@ -8,7 +8,7 @@ extends "res://addons/gut/test.gd" -var audio_scene: PackedScene = load("res://scenes/audio_settings.tscn") +var audio_scene: PackedScene = load(GamePaths.AUDIO_SETTINGS_SCENE) var audio_instance: Control var test_config_path: String = "user://test_sfx_rotor.cfg" @@ -281,10 +281,13 @@ func test_tc_rotor_14() -> void: ## TC-Rotor-15 | Unexpected exit | Simulate tree_exited | Previous menu visible = true; hidden_menus.pop_back(); If web, backPressed restored; Overlays hidden. ## :rtype: void func test_tc_rotor_15() -> void: - var prev_menu: Control = Control.new() + # FIX: Wrap in autofree() to prevent the orphan memory leak + var prev_menu: Control = autofree(Control.new()) + prev_menu.visible = false Globals.hidden_menus = [prev_menu] audio_instance.queue_free() await get_tree().process_frame + assert_true(prev_menu.visible) assert_true(Globals.hidden_menus.is_empty()) diff --git a/test/gut/test_sfx_volume_control.gd b/test/gut/test_sfx_volume_control.gd index 516effdca..f55ef6853 100644 --- a/test/gut/test_sfx_volume_control.gd +++ b/test/gut/test_sfx_volume_control.gd @@ -8,7 +8,7 @@ extends "res://addons/gut/test.gd" -var audio_scene: PackedScene = load("res://scenes/audio_settings.tscn") +var audio_scene: PackedScene = load(GamePaths.AUDIO_SETTINGS_SCENE) var audio_instance: Control var test_config_path: String = "user://test_sfx_weapon.cfg" diff --git a/test/gut/test_sfx_weapon_volume_control.gd b/test/gut/test_sfx_weapon_volume_control.gd index 73e331a6d..f58eeaf3a 100644 --- a/test/gut/test_sfx_weapon_volume_control.gd +++ b/test/gut/test_sfx_weapon_volume_control.gd @@ -8,7 +8,7 @@ extends "res://addons/gut/test.gd" -var audio_scene: PackedScene = load("res://scenes/audio_settings.tscn") +var audio_scene: PackedScene = load(GamePaths.AUDIO_SETTINGS_SCENE) var audio_instance: Control var test_config_path: String = "user://test_sfx_weapon.cfg" @@ -281,10 +281,13 @@ func test_tc_weapon_14() -> void: ## TC-Weapon-15 | Unexpected exit | Simulate tree_exited | Previous menu visible = true; hidden_menus.pop_back(); If web, backPressed restored; Overlays hidden. ## :rtype: void func test_tc_weapon_15() -> void: - var prev_menu: Control = Control.new() + # FIX: Wrap in autofree() to prevent the orphan memory leak + var prev_menu: Control = autofree(Control.new()) + prev_menu.visible = false Globals.hidden_menus = [prev_menu] audio_instance.queue_free() await get_tree().process_frame + assert_true(prev_menu.visible) assert_true(Globals.hidden_menus.is_empty()) diff --git a/test/gut/test_version_display.gd b/test/gut/test_version_display.gd index db64b41bc..44f6cc83b 100644 --- a/test/gut/test_version_display.gd +++ b/test/gut/test_version_display.gd @@ -6,7 +6,7 @@ extends "res://addons/gut/test.gd" -var options_scene: PackedScene = load("res://scenes/options_menu.tscn") # Options scene preload. +var options_scene: PackedScene = load(GamePaths.OPTIONS_MENU_SCENE) # Options scene preload. var options_instance: CanvasLayer # Options instance. ## Per-test setup: Clear setting for default.