extends Node const SAVE_PATH = "user://settings.json" var default_bindings = { "jump": {"type": "key", "code": KEY_SPACE}, "crouch": {"type": "key", "code": KEY_CTRL}, "dash": {"type": "key", "code": KEY_SHIFT}, "fire": {"type": "mouse", "code": MOUSE_BUTTON_LEFT}, "fire_alt": {"type": "mouse", "code": MOUSE_BUTTON_RIGHT}, "grapple": {"type": "mouse", "code": MOUSE_BUTTON_MIDDLE}, "reload": {"type": "key", "code": KEY_R}, "interact": {"type": "key", "code": KEY_E}, "melee": {"type": "key", "code": KEY_F}, "move_forward": {"type": "key", "code": KEY_W}, "move_back": {"type": "key", "code": KEY_S}, "move_left": {"type": "key", "code": KEY_A}, "move_right": {"type": "key", "code": KEY_D}, "weapon_next": {"type": "mouse", "code": MOUSE_BUTTON_WHEEL_UP}, "weapon_prev": {"type": "mouse", "code": MOUSE_BUTTON_WHEEL_DOWN}, "weapon_1": {"type": "key", "code": KEY_1}, "weapon_2": {"type": "key", "code": KEY_2}, "weapon_3": {"type": "key", "code": KEY_3}, "weapon_4": {"type": "key", "code": KEY_4}, "toggle_flashlight": {"type": "key", "code": KEY_T}, } var current_bindings = {} var show_debug_ui: bool = false var show_movement_speed: bool = false var username: String = "Player" var mouse_sensitivity: float = 0.002 var ads_sensitivity: float = 0.001 var world_fov: float = 90.0 # Video Settings var display_mode: int = DisplayServer.WINDOW_MODE_WINDOWED var resolution_index: int = 2 # Default to 1920x1080 var fps_cap: int = 0 var show_fps: bool = false # Audio Settings var vol_master: float = 100.0 var vol_sfx: float = 100.0 var vol_music: float = 100.0 var vol_wind: float = 100.0 var resolutions = [ Vector2i(640, 480), # 480p Vector2i(1280, 720), # 720p Vector2i(1920, 1080), # 1080p Vector2i(2560, 1080), # UW-1080p Vector2i(2560, 1440), # 1440p Vector2i(3440, 1440), # UW-1440p Vector2i(3840, 2160) # 4K ] func _ready() -> void: # Initialize defaults current_bindings = default_bindings.duplicate(true) # Load overrides _load_settings() # Apply to InputMap _apply_all_bindings() # Apply Video Settings apply_video_settings() # Setup & Apply Audio Settings _setup_audio_buses() apply_audio_settings() func _setup_audio_buses() -> void: if AudioServer.bus_count < 4: # Assuming bus 0 is Master if AudioServer.get_bus_name(0) != "Master": AudioServer.set_bus_name(0, "Master") # SFX if AudioServer.bus_count <= 1: AudioServer.add_bus(1) AudioServer.set_bus_name(1, "SFX") # Music if AudioServer.bus_count <= 2: AudioServer.add_bus(2) AudioServer.set_bus_name(2, "Music") # Wind if AudioServer.bus_count <= 3: AudioServer.add_bus(3) AudioServer.set_bus_name(3, "Wind") func _apply_all_bindings() -> void: for action in current_bindings: if not InputMap.has_action(action): InputMap.add_action(action) InputMap.action_erase_events(action) var binding = current_bindings[action] var ev: InputEvent if binding["type"] == "key": ev = InputEventKey.new() ev.keycode = binding["code"] elif binding["type"] == "mouse": ev = InputEventMouseButton.new() ev.button_index = binding["code"] if ev: InputMap.action_add_event(action, ev) func rebind_action(action: String, event: InputEvent) -> void: if not current_bindings.has(action): return if event is InputEventKey: var code = event.physical_keycode if event.physical_keycode != 0 else event.keycode current_bindings[action] = {"type": "key", "code": code} elif event is InputEventMouseButton: current_bindings[action] = {"type": "mouse", "code": event.button_index} else: return _apply_all_bindings() _save_settings() func reset_to_defaults() -> void: current_bindings = default_bindings.duplicate(true) _apply_all_bindings() _save_settings() func _save_settings() -> void: var save_data = { "bindings": current_bindings, "show_debug_ui": show_debug_ui, "show_movement_speed": show_movement_speed, "username": username, "mouse_sensitivity": mouse_sensitivity, "ads_sensitivity": ads_sensitivity, "world_fov": world_fov, "display_mode": display_mode, "resolution_index": resolution_index, "fps_cap": fps_cap, "show_fps": show_fps, "vol_master": vol_master, "vol_sfx": vol_sfx, "vol_music": vol_music, "vol_wind": vol_wind } var json_string = JSON.stringify(save_data) var file = FileAccess.open(SAVE_PATH, FileAccess.WRITE) if file: file.store_string(json_string) file.close() func _load_settings() -> void: if FileAccess.file_exists(SAVE_PATH): var file = FileAccess.open(SAVE_PATH, FileAccess.READ) if file: var json_string = file.get_as_text() file.close() var json = JSON.new() var error = json.parse(json_string) if error == OK: var data = json.get_data() if data is Dictionary: if data.has("bindings"): var loaded_bindings = data["bindings"] for action in loaded_bindings: if current_bindings.has(action): current_bindings[action] = loaded_bindings[action] if data.has("username"): username = data["username"] if data.has("show_debug_ui"): show_debug_ui = data["show_debug_ui"] if data.has("show_movement_speed"): show_movement_speed = data["show_movement_speed"] if data.has("mouse_sensitivity"): mouse_sensitivity = data["mouse_sensitivity"] if data.has("ads_sensitivity"): ads_sensitivity = data["ads_sensitivity"] if data.has("world_fov"): world_fov = data["world_fov"] if data.has("display_mode"): display_mode = data["display_mode"] if data.has("resolution_index"): resolution_index = data["resolution_index"] if data.has("fps_cap"): fps_cap = data["fps_cap"] if data.has("show_fps"): show_fps = data["show_fps"] if data.has("vol_master"): vol_master = data["vol_master"] if data.has("vol_sfx"): vol_sfx = data["vol_sfx"] if data.has("vol_music"): vol_music = data["vol_music"] if data.has("vol_wind"): vol_wind = data["vol_wind"] func apply_audio_settings() -> void: _set_bus_volume("Master", vol_master) _set_bus_volume("SFX", vol_sfx) _set_bus_volume("Music", vol_music) _set_bus_volume("Wind", vol_wind) func _set_bus_volume(bus_name: String, vol_100: float) -> void: var bus_idx = AudioServer.get_bus_index(bus_name) if bus_idx >= 0: var linear = clampf(vol_100 / 100.0, 0.0001, 1.0) if vol_100 <= 0.1: AudioServer.set_bus_mute(bus_idx, true) else: AudioServer.set_bus_mute(bus_idx, false) AudioServer.set_bus_volume_db(bus_idx, linear_to_db(linear)) func apply_video_settings() -> void: if display_mode == DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN: DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN) elif display_mode == DisplayServer.WINDOW_MODE_FULLSCREEN: DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN) else: DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) if resolution_index >= 0 and resolution_index < resolutions.size(): DisplayServer.window_set_size(resolutions[resolution_index]) # Center window on primary screen var screen = DisplayServer.window_get_current_screen() var screen_size = DisplayServer.screen_get_size(screen) var window_size = DisplayServer.window_get_size() # Simple centering heuristic DisplayServer.window_set_position(Vector2i((screen_size - window_size) / 2.0)) Engine.max_fps = fps_cap