extends Node const SETTING_USAGE_FLAG := PROPERTY_USAGE_SCRIPT_VARIABLE | PROPERTY_USAGE_EDITOR const CONFIG_PATH := "user://settings.cfg" @export_group("Gameplay") @export_subgroup("Camera") @export var camera_fov: float = 75: set(value): camera_fov = value @export_group("Video") @export_subgroup("Display") @export var fullscreen: bool = false: set(value): DisplayServer.window_set_mode( ( DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN if value else DisplayServer.WINDOW_MODE_WINDOWED ) ) fullscreen = value @export var vsync: DisplayServer.VSyncMode = DisplayServer.VSYNC_DISABLED: set(value): DisplayServer.window_set_vsync_mode(value) vsync = value @export var limit_max_fps: bool = false: set(value): if value: Engine.max_fps = max_fps else: Engine.max_fps = 0 limit_max_fps = value @export var max_fps: int = 60: set(value): if limit_max_fps: Engine.max_fps = value else: Engine.max_fps = 0 max_fps = value @export_subgroup("Quality") @export var resolution_scale: float = 1: set(value): get_viewport().scaling_3d_scale = value resolution_scale = value @export var filtering: Viewport.Scaling3DMode = Viewport.SCALING_3D_MODE_BILINEAR: set(value): get_viewport().scaling_3d_mode = value filtering = value @export_group("UI") @export var ui_scale: float = 1: set(value): get_tree().root.content_scale_factor = value ui_scale = value @export_group("Audio") @export_group("Controls") @export_subgroup("KB & Mouse") ## rotation speed in degrees per pixel @export var mouse_sensitivity: float = 0.5 @export var invert_mouse_horizontal: bool = false @export var invert_mouse_vertical: bool = false @export_subgroup("Controller") ## horizontal rotation speed in degrees per second @export var controller_sensitivity_horizontal: float = 270 ## vertical rotation speed in degrees per second @export var controller_sensitivity_vertical: float = 120 @export var invert_controller_horizontal: bool = false @export var invert_controller_vertical: bool = false var _default_values: Dictionary = {} func _init() -> void: for property in get_property_list(): if _property_is_setting(property): var property_name := StringName(property["name"] as String) _default_values[property_name] = get(property_name) func _ready() -> void: process_mode = Node.PROCESS_MODE_ALWAYS load_config() func _input(event: InputEvent) -> void: if event.is_action_pressed("toggle_fullscreen"): fullscreen = not fullscreen func set_defaults() -> void: for property_name: StringName in _default_values.keys(): set(property_name, _default_values[property_name]) var refresh_rate := DisplayServer.screen_get_refresh_rate() if refresh_rate != -1: max_fps = refresh_rate as int func save_config() -> void: var config := ConfigFile.new() var section: String = "" for property in get_property_list(): if _property_is_section(property): section = property["name"] as String if _property_is_setting(property): var property_name := StringName(property["name"] as String) config.set_value(section, property_name, get(property_name)) var err := config.save(CONFIG_PATH) if err != OK: printerr("failed to save config file: %s" % err) func load_config() -> void: var config := ConfigFile.new() var err := config.load(CONFIG_PATH) if err == ERR_FILE_NOT_FOUND: set_defaults() save_config() return if err != OK: printerr("failed to load config file: %s" % err) return var section: String = "" for property in get_property_list(): if _property_is_section(property): section = property["name"] as String if _property_is_setting(property): var property_name := StringName(property["name"] as String) if not config.has_section_key(section, property_name): print("%s not found in config, setting default" % property_name) set(property_name, _default_values[property_name]) continue var value: Variant = config.get_value(section, property_name) set(property_name, value) func _property_is_setting(property: Dictionary) -> bool: return (property["usage"] as int & SETTING_USAGE_FLAG) == SETTING_USAGE_FLAG func _property_is_section(property: Dictionary) -> bool: return property["usage"] == PROPERTY_USAGE_GROUP