extends Node # SettingsManager - Global singleton for handling game settings persistence # Autoloaded as "SettingsManager" const SETTINGS_FILE = "user://game_settings.cfg" const RESOLUTIONS = [ Vector2i(1024, 576), Vector2i(1280, 720), Vector2i(1366, 768), Vector2i(1600, 900), Vector2i(1920, 1080), Vector2i(2560, 1440) ] # Default values var settings = { "video": { "fullscreen": true, "vsync": true, "resolution_idx": 1, "msaa": 0, # 0: Disabled, 1: 2x, 2: 4x, 3: 8x "shadow_quality": 2, # 0: Low, 1: Med, 2: High, 3: Ultra "fps_cap": 2 # 0: Unlimited, 1: 30, 2: 60, 3: 120, 4: 144 }, "audio": { "master_volume": 0.8, "music_volume": 0.7, "sfx_volume": 0.9 }, "controls": { "use_controller": false, # Movement (Keybinds are typically fixed to WASD/Arrows in Input Map, but we show them) "move_up": KEY_W, "move_down": KEY_S, "move_left": KEY_A, "move_right": KEY_D, # Actions "grab": KEY_SPACE, "put": KEY_R, "tekton_grab": KEY_G, # Power-Up Controls "powerup_1": KEY_1, "powerup_2": KEY_2, "powerup_3": KEY_3, "powerup_4": KEY_4, # Power Bar Controls / Special "attack_mode": KEY_Q, "spawn_boost": KEY_E } } signal settings_applied signal control_remapped(action: String, key: int) func _ready(): load_settings() apply_all_settings() func load_settings(): var config = ConfigFile.new() var err = config.load(SETTINGS_FILE) if err == OK: for section in settings.keys(): for key in settings[section].keys(): settings[section][key] = config.get_value(section, key, settings[section][key]) print("[Settings] Loaded.") else: print("[Settings] Using defaults.") func save_settings(): var config = ConfigFile.new() for section in settings.keys(): for key in settings[section].keys(): config.set_value(section, key, settings[section][key]) config.save(SETTINGS_FILE) func apply_all_settings(): apply_video_settings() apply_audio_settings() emit_signal("settings_applied") func apply_video_settings(): var video = settings.video if video.fullscreen: DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN) else: DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) # Apply resolution size when windowed var res_idx = video.resolution_idx if res_idx >= 0 and res_idx < RESOLUTIONS.size(): var target_res = RESOLUTIONS[res_idx] DisplayServer.window_set_size(target_res) # Center window after resize var screen = DisplayServer.window_get_current_screen() var screen_rect = DisplayServer.screen_get_usable_rect(screen) var window_size = DisplayServer.window_get_size() DisplayServer.window_set_position(screen_rect.position + (screen_rect.size - window_size) / 2) if video.vsync: DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED) else: DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED) # Apply MSAA var viewport = get_viewport() match video.msaa: 0: viewport.msaa_3d = Viewport.MSAA_DISABLED 1: viewport.msaa_3d = Viewport.MSAA_2X 2: viewport.msaa_3d = Viewport.MSAA_4X 3: viewport.msaa_3d = Viewport.MSAA_8X # Apply FPS Cap match video.fps_cap: 0: Engine.max_fps = 0 1: Engine.max_fps = 30 2: Engine.max_fps = 60 3: Engine.max_fps = 120 4: Engine.max_fps = 144 # Apply Shadow Quality (Simplified for Forward+) match video.shadow_quality: 0: # Low RenderingServer.directional_soft_shadow_filter_set_quality(RenderingServer.SHADOW_QUALITY_SOFT_LOW) RenderingServer.positional_soft_shadow_filter_set_quality(RenderingServer.SHADOW_QUALITY_SOFT_LOW) 1: # Medium RenderingServer.directional_soft_shadow_filter_set_quality(RenderingServer.SHADOW_QUALITY_SOFT_MEDIUM) RenderingServer.positional_soft_shadow_filter_set_quality(RenderingServer.SHADOW_QUALITY_SOFT_MEDIUM) 2: # High RenderingServer.directional_soft_shadow_filter_set_quality(RenderingServer.SHADOW_QUALITY_SOFT_HIGH) RenderingServer.positional_soft_shadow_filter_set_quality(RenderingServer.SHADOW_QUALITY_SOFT_HIGH) 3: # Ultra RenderingServer.directional_soft_shadow_filter_set_quality(RenderingServer.SHADOW_QUALITY_SOFT_ULTRA) RenderingServer.positional_soft_shadow_filter_set_quality(RenderingServer.SHADOW_QUALITY_SOFT_ULTRA) func apply_audio_settings(): var audio = settings.audio set_bus_volume("Master", audio.master_volume) set_bus_volume("Music", audio.music_volume) set_bus_volume("SFX", audio.sfx_volume) func set_bus_volume(bus_name: String, volume_linear: float): var bus_idx = AudioServer.get_bus_index(bus_name) if bus_idx != -1: AudioServer.set_bus_volume_db(bus_idx, linear_to_db(volume_linear)) AudioServer.set_bus_mute(bus_idx, volume_linear <= 0.001) func set_control(action_name: String, keycode: int): if settings.controls.has(action_name): settings.controls[action_name] = keycode emit_signal("control_remapped", action_name, keycode) save_settings() func get_control_keycode(action_name: String) -> int: return settings.controls.get(action_name, -1) func get_control_text(action_name: String) -> String: var code = get_control_keycode(action_name) if code == -1: return "Unbound" return OS.get_keycode_string(code)