diff options
Diffstat (limited to 'client/system/settings.gd')
-rw-r--r-- | client/system/settings.gd | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/client/system/settings.gd b/client/system/settings.gd new file mode 100644 index 00000000..1cacfce9 --- /dev/null +++ b/client/system/settings.gd @@ -0,0 +1,200 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, version 3 of the License only. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. +# +extends Node +class_name Settings + +static func get_root(): + return SettingsRoot.new([ + SettingsCategory.new("gameplay", [ + ToggleSetting.new("usernames", true), + ToggleSetting.new("latch_boost", true), + ToggleSetting.new("vibration", true), + ToggleSetting.new("invert_camera", false), + ToggleSetting.new("interpolate_camera_rotation", false), + ButtonSetting.new("setup_completed", false, launch_setup), + ToggleSetting.new("tutorial_disabled", false), + ToggleSetting.new("hints_started", false), + ToggleSetting.new("accessible_movement", false), + ToggleSetting.new("first_person", false), + DropdownSetting.new("interact_target", "dirsnap", ["dir", "dirsnap"]), + ]), + SettingsCategory.new("graphics", [ + PresetRow.new("preset", { + "low": {"ui_blur": true, "aa": "disabled", "ssao": false, "taa": false, "shadows": false, "glow": false, "grass_amount": 0, "lq_trees": true}, + "medium": {"ui_blur": true, "aa": "fx", "ssao": false, "taa": false, "shadows": true, "glow": false, "grass_amount": 16, "lq_trees": false}, + "high": {"ui_blur": true, "aa": "ms2x", "ssao": true, "taa": false, "shadows": true, "glow": true, "grass_amount": 24, "lq_trees": false} + }), + DropdownSetting.new("fullscreen", "keep", ["keep", "always", "never"]), + DropdownSetting.new("aa", "ms2x" if Global.on_high_end() else "disabled", ["disabled", "fx", "ms2x", "ms4x"]), + ToggleSetting.new("ssao", true if Global.on_high_end() else false), + ToggleSetting.new("taa", false), + DropdownSetting.new("gi", "disabled", ["disabled", "sdfgi", "voxelgi"]), + ToggleSetting.new("shadows", true if Global.on_high_end() else false), + ToggleSetting.new("glow", true if Global.on_high_end() else false), + RangeSetting.new("grass_amount", 24 if Global.on_high_end() else 0, 0, 32, false), + ToggleSetting.new("lq_trees", false if Global.on_high_end() else true), + ToggleSetting.new("debug_info", false), + ToggleSetting.new("ui_blur", true) + ]), + SettingsCategory.new("audio", [ + RangeSetting.new("master_volume", 0, -30, 0), + RangeSetting.new("music_volume", 0, -30, 0), + RangeSetting.new("sfx_volume", 0, -30, 0), + ]), + SettingsCategory.new("ui", [ + DropdownSetting.new("touch_controls", "automatic", ["automatic", "enabled", "disabled"]), + DropdownSetting.new("language", "system", Global.language_list()), + ToggleSetting.new("hide_overlays", false), + DropdownSetting.new("scale_mode", "resize", ["resize", "disabled"]), + RangeSetting.new("scale_factor", 1. if not Global.on_mobile() else 1.5, 0.5, 1.5, 3), + ]), + SettingsCategory.new("input", InputManager.settings()), + SettingsCategory.new("server", [ + PathSetting.new("binary_path", "", FileDialog.FileMode.FILE_MODE_OPEN_FILE), + PathSetting.new("editor_binary_path", "", FileDialog.FileMode.FILE_MODE_OPEN_DIR), + PathSetting.new("data_path", "", FileDialog.FileMode.FILE_MODE_OPEN_DIR), + TextSetting.new("name", "A Hurry Curry! Server"), + ToggleSetting.new("allow_external_connections", true), + ToggleSetting.new("enable_ipv6", true), + NumberSetting.new("bind_port", 27032, 1, 65535), + ToggleSetting.new("upnp", false), + ToggleSetting.new("mdns", true), + ToggleSetting.new("register", false), + ]), + SettingsCategory.new("online", [ + ToggleSetting.new("use_registry", false), + TextSetting.new("registry_url", "https://hurrycurry-registry.metamuffin.org/"), + ToggleSetting.new("use_discover", true), + PathSetting.new("discover_binary", "", FileDialog.FileMode.FILE_MODE_OPEN_FILE), + ]) + ]) + +static var tree: GameSetting +static var values: Dictionary = {} +static var loaded_path: String + +static func read(key: String): + if !values.has(key): + push_error("Tried to access setting \"%s\", which does not exist (missing key)" % key) + return null + return values[key] + +static func write_unchecked(key: String, value): + value = value.duplicate(true) if value is Array else value + if key in values and typeof(values[key]) == typeof(value) and not value is Array and values[key] == value: return + values[key] = value + trigger_hook(key, value) + +static func write(key: String, value): + if !values.has(key): + push_error("Tried to set setting \"%s\", which does not yet exist (missing key)" % key) + return + else: write_unchecked(key, value) + +static func load(path: String): + tree = get_root() + loaded_path = path + var changed = {} + if FileAccess.file_exists(path): + var f = FileAccess.open(path, FileAccess.READ) + changed = JSON.parse_string(f.get_as_text()) + tree.load(changed) + +static func save(): + var changed = {} + tree.save(changed) + var f = FileAccess.open(loaded_path, FileAccess.WRITE) + f.store_string(JSON.stringify(changed)) + +static func trigger_hook(key: String, value): + if change_hooks_display.get(key) != null: change_hooks_display.get(key).callv([value] if value != null else []) + if change_hooks_apply.get(key) != null: change_hooks_apply.get(key).callv([value] if value != null else []) + if key.find(".") != -1: trigger_hook(key.rsplit(".", false, 1)[0], null) + +static func hook_changed(key: String, display: bool, callable: Callable): + if display: change_hooks_display[key] = callable + else: change_hooks_apply[key] = callable + +static func hook_changed_init(key: String, display: bool, callable: Callable): + hook_changed(key, display, callable) + callable.call(read(key)) + +static func get_category_dict(prefix: String): + var map = {} + for k in values.keys(): + var kn = k.trim_prefix(prefix + ".") + if kn == k: continue + map[kn] = read(k) + return map + +static func launch_setup(): + Global.focused_menu.submenu("res://gui/menus/setup/setup.tscn") + +static var change_hooks_display = {} +static var change_hooks_apply = { + "input": h_input, + "gameplay.hints_started": h_hints_started, + "graphics.aa": h_aa, + "graphics.taa": h_taa, + "graphics.fullscreen": h_fullscreen, + "ui.scale_mode": h_scale_mode, + "ui.scale_factor": h_scale_factor, + "ui.language": h_language, + "audio.master_volume": h_volume_master, + "audio.music_volume": h_volume_music, + "audio.sfx_volume": h_volume_sfx, +} + +static func h_aa(_mode): + Global.configure_viewport_aa(Global.get_viewport()) + +static func h_taa(enabled): + Global.get_viewport().use_taa = enabled + +static func h_scale_mode(mode: String): + var root = Global.get_tree().root + if OS.has_feature("movie"): + root.content_scale_mode = Window.CONTENT_SCALE_MODE_VIEWPORT + root.content_scale_aspect = Window.CONTENT_SCALE_ASPECT_KEEP + else: match mode: + "resize": root.content_scale_mode = Window.CONTENT_SCALE_MODE_CANVAS_ITEMS + "disabled": root.content_scale_mode = Window.CONTENT_SCALE_MODE_DISABLED + +static func h_scale_factor(value: float): + Global.get_tree().root.content_scale_factor = value + +static func h_volume_master(value: float): Sound.set_volume(0, value) +static func h_volume_music(value: float): Sound.set_volume(1, value) +static func h_volume_sfx(value: float): Sound.set_volume(2, value) + +static func h_language(language: String): + if language == "system": language = OS.get_locale_language() + TranslationServer.set_locale(language) + +static func h_fullscreen(mode: String): + match mode: + "keep": pass + "always": DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN) + "never": if DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_FULLSCREEN: + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) + +static func h_input(): + InputManager.apply_input_map(Settings.get_category_dict("input")) + +static func h_hints_started(started: bool): + if not started: + for k in Profile.values["hints"].keys(): + Profile.set_hint(k, false) |