# Undercooked - a game about cooking # Copyright 2024 metamuffin # Copyright 2024 tpart # Copyright 2024 nokoe # # 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 . # extends Node # Each setting contains a dictionary with the following keys: # "type": The type of the setting. Can be "toggle", "line", "range", "dropdown", or "dropdown_preset" # "value": The value of the setting. When using "dropdown", this contains an int # as the index for the respecitve element in the "options" array. # (Optional: Only when type "dropdown_preset") "apply": A dictionary to override all other settings. # (Optional: Only when type "range") "min" and "max": The min and max values. # "description": The setting name displayed in the settings menu. # (Optional: Only when type "dropdown") "options": An array which contains all # possible values. var default_profile := { "username": "Giovanni", "character": 0, "last_server_url": "" } var default_settings := { "language": { "type": "dropdown", "options": [tr("System default"), "en", "de"], "value": 0, "description": tr("Language"), }, "fullscreen": { "type": "toggle", "value": false, "description": tr("Fullscreen") }, "interpolate_camera_rotation": { "type": "toggle", "value": true, "description": tr("Interpolate the camera rotation") }, "invert_camera": { "type": "toggle", "value": false, "description": tr("Invert camera movement") }, "server_binary": { "type": "line", "value": "", "description": tr("Server binary (leave empty to search PATH)") }, "server_data": { "type": "line", "value": "", "description": tr("Server data directory (leave empty to auto-detect)") }, "graphics_preset": { "type": "dropdown_preset", "options": [tr("Low"), tr("Medium"), tr("High"), tr("Ultra")], "value": 1, "description": tr("Graphics preset"), "apply": "GRAPHICS_PRESETS" }, "voxel_gi": { "type": "toggle", "value": false, "description": tr("Use VoxelGI (Blocks the game on map update, looks good)") }, "sdfgi": { "type": "toggle", "value": false, "description": tr("Use SDFGI (Doesn't block the game, looks worse)") }, "grass_amount": { "type": "range", "value": 16, "min": 0, "max": 32, "description": tr("3D grass amount per grass tile") }, "setup_complete": { "type": "toggle", "value": false, "description": tr("Initial setup complete. (Uncheck and restart to reenter)") } } const GRAPHICS_PRESETS = [ # Low: { "voxel_gi": false, "sdfgi": false, "grass_amount": 0 }, # Medium: { "voxel_gi": false, "sdfgi": false, "grass_amount": 16 }, # High: { "voxel_gi": false, "sdfgi": true, "grass_amount": 16 }, # Ultra: { "voxel_gi": true, "sdfgi": false, "grass_amount": 16 } ] var profile: Dictionary var settings: Dictionary var server_url = "" var error_message = "" var focus_auto_changed := false var fade_next := false # Set true when transitioning from another scene (fade in requried) func _init(): profile = load_dict("user://profile", default_profile) settings = load_dict("user://settings", default_settings) update_fullscreen() update_language() func _input(event): if Input.is_action_just_pressed("fullscreen"): Global.set_setting("fullscreen", not Global.get_setting("fullscreen")) save_settings() update_fullscreen() func update_language(): var lang_idx = get_setting("language") var lang = settings["language"]["options"][lang_idx] if lang_idx != 0: # 0 is system language TranslationServer.set_locale(lang) else: TranslationServer.set_locale(OS.get_locale_language()) func update_fullscreen(): if get_setting("fullscreen"): DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN) else: DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) func save_profile(): save_dict("user://profile", profile) func save_settings(): save_dict("user://settings", settings) func save_dict(path: String, dict: Dictionary): var f = FileAccess.open(path, FileAccess.WRITE) f.store_var(dict.duplicate(true)) func load_dict(path: String, default: Dictionary) -> Dictionary: # TOCTOU here. Godot docs says its fine. if not FileAccess.file_exists(path): print("Skip profile load") return default var f = FileAccess.open(path, FileAccess.READ) var saved_dict = f.get_var() if saved_dict != null and saved_dict is Dictionary: add_missing_keys(saved_dict, default) print("Loaded dict: ", saved_dict) return saved_dict func on_vulkan() -> bool: return ProjectSettings.get_setting("rendering/rendering_device/driver") == "vulkan" func focus_first_button(node: Node) -> bool: focus_auto_changed = true if node is Button: node.grab_focus() print("Node %s (%s) was selected for focus" % [node.name, node]) return true for c in node.get_children(): if focus_first_button(c): return true return false func connect_button_sounds(node: Node): if node is Button: node.pressed.connect(Sound.play_click) if node is Button or node is LineEdit or node is Slider: node.mouse_entered.connect(Sound.play_hover) for c in node.get_children(): connect_button_sounds(c) func add_missing_keys(dict: Dictionary, reference: Dictionary): for k in reference.keys(): if !dict.has(k): dict[k] = reference[k] else: if dict[k] is Dictionary: add_missing_keys(dict[k], reference[k]) func get_setting(key: String): if settings.has(key): return settings[key]["value"] else: push_error("Tried to access setting \"%s\", which does not exist (missing key)" % key) return null func set_setting(key: String, value): if !settings.has(key): push_error("Tried to set setting \"%s\", which does not yet exist (missing key)" % key) settings[key] = {} settings[key]["value"] = value