diff options
Diffstat (limited to 'client')
42 files changed, 439 insertions, 564 deletions
diff --git a/client/global.gd b/client/global.gd index d62f5413..0923b457 100644 --- a/client/global.gd +++ b/client/global.gd @@ -19,7 +19,6 @@ class_name G extends Node -signal settings_changed() signal using_joypad_change(using: bool) signal using_touch_change(using: bool) @@ -39,90 +38,16 @@ var default_profile := { "has_seen_join_while_running": false } } -var languages := language_array() + var using_joypad := false var using_touch := false -@onready var default_settings := [ - SettingsCategory.new(tr("Gameplay"), "gameplay", { - "touch_controls": DropdownSetting.new(tr("Enable touch screen controls"), 0, [tr("Automatic"), tr("Enabled"), tr("Disabled")]), - "interpolate_camera_rotation": ToggleSetting.new(tr("Smooth camera rotation"), false), - "invert_camera": ToggleSetting.new(tr("Invert camera movement"), false), - "usernames": ToggleSetting.new(tr("Show username tags"), true), - "setup_complete": ToggleSetting.new(tr("Initial setup complete. (Uncheck and restart to reenter)"), false), - "tutorial_started": ToggleSetting.new(tr("Tutorial started"), false), - "latch_boost": ToggleSetting.new(tr("Always extend boost to maximum duration"), true), - }), - SettingsCategory.new(tr("Graphics"), "graphics", { - "fullscreen": DropdownSetting.new(tr("Fullscreen"), 0, [tr("Keep"), tr("Always"), tr("Never")]), - "aa": DropdownSetting.new(tr("Anti-aliasing"), 2 if on_high_end() else 0, [tr("Disabled"), "FXAA", "MSAA 2x", "MSAA 4x"]), - "ssao": ToggleSetting.new(tr("Ambient occlusion"), true if on_high_end() else false), - "taa": ToggleSetting.new(tr("Temporal Anti-Aliasing"), false), - "gi": DropdownSetting.new(tr("Global illumination"), 0, [tr("Disabled"), tr("SDFGI"), tr("Voxel GI")]), - "shadows": ToggleSetting.new(tr("Enable shadows"), true if on_high_end() else false), - "glow": ToggleSetting.new(tr("Enable glow"), true if on_high_end() else false), - "grass_amount": RangeSetting.new(tr("3D grass amount per grass tile"), 16 if on_high_end() else 0, 0, 32, false), - "lq_trees": ToggleSetting.new(tr("Low-poly trees"), false if on_high_end() else true), - "ui_blur": ToggleSetting.new(tr("Enable UI blur"), true) - }, [ - Preset.new(tr("Graphics"), { - tr("Low"): { - "ui_blur": true, - "aa": 0, - "ssao": false, - "taa": false, - "shadows": false, - "glow": false, - "grass_amount": 0, - "lq_trees": true - }, - tr("Medium"): { - "ui_blur": true, - "aa": 1, - "ssao": false, - "taa": false, - "shadows": true, - "glow": false, - "grass_amount": 0, - "lq_trees": false - }, - tr("High"): { - "ui_blur": true, - "aa": 2, - "ssao": true, - "taa": false, - "shadows": true, - "glow": true, - "grass_amount": 16, - "lq_trees": false - } - }) - ]), - SettingsCategory.new(tr("Audio"), "audio", { - "master_volume": RangeSetting.new(tr("Master Volume"), 0, -30, 0), - "music_volume": RangeSetting.new(tr("Music Volume"), 0, -30, 0), - "sfx_volume": RangeSetting.new(tr("SFX Volume"), 0, -30, 0), - }), - SettingsCategory.new(tr("User interface"), "ui", { - "language": DropdownSetting.new(tr("Language"), 0, languages.map(func(e): return e[1])), - "ui_scale_mode": DropdownSetting.new(tr("UI scale mode"), 0, [tr("Resize"), tr("Disabled")]), - "ui_scale_factor": RangeSetting.new(tr("UI scale factor"), 1. if not on_mobile() else 1.5, 0.5, 1.5, 3), - }), - SettingsCategory.new(tr("Controls"), "input", - InputManager.input_map_to_settings_dictionary(InputManager.default_input_map) - ), - SettingsCategory.new(tr("Other"), "other", { - "server_binary": TextSetting.new(tr("Server binary (leave empty to search PATH)"), "", tr("Enter path")), - "server_data": TextSetting.new(tr("Server data directory (leave empty to auto-detect)"), "", tr("Enter path")), - "debug_info": ToggleSetting.new(tr("Display debug info (Framerate, etc.)"), false), - }) - ] - -# Profile and settings are stored in a Dictionary[String, GameSetting] -# Unlike the default settings, the settings dictionary is not split into categories. +# profile and settings are stored in a Dictionary[String, Any] var profile: Dictionary var settings: Dictionary +var settings_tree: GameSetting + var server_url = "" var error_message = "" @@ -131,28 +56,20 @@ var focused_node: Control func _ready(): profile = load_dict("user://profile", default_profile) load_settings("user://settings") - apply_settings() get_viewport().gui_focus_changed.connect(Sound.play_hover_maybe) get_viewport().gui_focus_changed.connect(func(node): focused_node = node) func _input(event): if Input.is_action_just_pressed("fullscreen"): - match Global.get_setting("fullscreen"): - 0: - # Keep setting + match Global.get_setting("graphics.fullscreen"): + "keep": if DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_FULLSCREEN: DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) else: DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN) - 1: - # Always - set_setting("fullscreen", 2) # Set to never - update_fullscreen() - 2: - # Never - set_setting("fullscreen", 1) # Set to always - update_fullscreen() + "always": set_setting("graphics.fullscreen", "never") + "never": set_setting("graphics.fullscreen", "always") # Update using_joypad variable if event is InputEventMouseButton or event is InputEventKey: @@ -165,7 +82,7 @@ func _input(event): using_joypad_change.emit(using_joypad) # Update using_touch variable - if get_setting("touch_controls") == 0: # Only if set to automatic + if get_setting("ui.touch_controls") == "automatic": # Only if set to automatic if event is InputEventScreenTouch or event is InputEventScreenDrag: if not using_touch: using_touch = true @@ -175,89 +92,11 @@ func _input(event): using_touch = false using_touch_change.emit(using_touch) -func apply_settings(): - update_fullscreen() - update_language() - - # Anti-aliasing - match get_setting("aa"): - 0: - get_viewport().msaa_2d = Viewport.MSAA_DISABLED - get_viewport().msaa_3d = Viewport.MSAA_DISABLED - get_viewport().screen_space_aa = Viewport.SCREEN_SPACE_AA_DISABLED - 1: - get_viewport().msaa_2d = Viewport.MSAA_DISABLED - get_viewport().msaa_3d = Viewport.MSAA_DISABLED - get_viewport().screen_space_aa = Viewport.SCREEN_SPACE_AA_FXAA - 2: - get_viewport().msaa_2d = Viewport.MSAA_2X - get_viewport().msaa_3d = Viewport.MSAA_2X - get_viewport().screen_space_aa = Viewport.SCREEN_SPACE_AA_DISABLED - 3: - get_viewport().msaa_2d = Viewport.MSAA_4X - get_viewport().msaa_3d = Viewport.MSAA_4X - get_viewport().screen_space_aa = Viewport.SCREEN_SPACE_AA_DISABLED - - # Temporal Anti-aliasing - get_viewport().use_taa = get_setting("taa") - - # UI scale mode - match get_setting("ui_scale_mode"): - 0: - get_tree().root.content_scale_mode = Window.CONTENT_SCALE_MODE_CANVAS_ITEMS - 1: - get_tree().root.content_scale_mode = Window.CONTENT_SCALE_MODE_DISABLED - - # UI scale factor - get_tree().root.content_scale_factor = get_setting("ui_scale_factor") - - # Hints - if not get_setting("tutorial_started"): - for k in profile["hints"].keys(): - set_hint(k, false) - - # Sets all volumes - Sound.set_volume(0, get_setting("master_volume")) - Sound.set_volume(1, get_setting("music_volume")) - Sound.set_volume(2, get_setting("sfx_volume")) - - # Touch controls - match get_setting("touch_controls"): - # 0: Automatically adjusted - 1: # Enabled - using_touch = true - 2: # Disabled - using_touch = false - using_touch_change.emit() - - # Input settings - InputManager.apply_input_map(InputManager.settings_dictionary_to_input_map(get_category_settings("input"))) - - settings_changed.emit() - -func update_language(): - var language = languages[get_setting("language")][0] - if language == "system": language = OS.get_locale_language() - TranslationServer.set_locale(language) - -func update_fullscreen(): - match get_setting("fullscreen"): - 0: pass - 1: DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN) - 2: if DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_FULLSCREEN: - DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) - func save_profile(): save_dict("user://profile", profile) func save_settings(): - for v in settings.values(): - v.fetch_setting() - var dict := {} - for k in settings.keys(): - var setting: GameSetting = settings[k] - dict[k] = setting.get_value() - save_dict("user://settings", dict) + save_dict("user://settings", settings) func save_dict(path: String, dict: Dictionary): var f = FileAccess.open(path, FileAccess.WRITE) @@ -271,29 +110,19 @@ func load_dict(path: String, default: Dictionary) -> Dictionary: return default var f = FileAccess.open(path, FileAccess.READ) var saved_dict = f.get_var(true) - - if saved_dict != null and saved_dict is Dictionary: - add_missing_keys(saved_dict, default) - return saved_dict func load_settings(path: String): - for category: SettingsCategory in default_settings.duplicate(true): - for k: String in category.settings.keys(): - settings[k] = category.settings[k] - - if not FileAccess.file_exists(path): - print("Skip settings load") - return - var f = FileAccess.open(path, FileAccess.READ) - var saved_dict = f.get_var(true) - - if saved_dict != null and saved_dict is Dictionary: - for k in settings.keys(): - if saved_dict.has(k) and typeof(settings[k].get_value()) == typeof(saved_dict[k]): - settings[k].set_value(saved_dict[k]) - - save_settings() # Save updated keys + if FileAccess.file_exists(path): + var f = FileAccess.open(path, FileAccess.READ) + settings = f.get_var(true) + else: + print("No settings file found.") + settings = {} + settings_tree = Settings.get_root() + settings_tree.check() + Settings.apply_initial() + save_settings() func on_mobile() -> bool: var os_name := OS.get_name() @@ -307,35 +136,24 @@ func on_high_end() -> bool: func on_vulkan() -> bool: return ProjectSettings.get_setting("rendering/rendering_device/driver") == "vulkan" -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].get_value() - else: + if !settings.has(key): push_error("Tried to access setting \"%s\", which does not exist (missing key)" % key) return null + return settings[key] -func get_category_settings(category_id: String): - for c: SettingsCategory in default_settings: - if c.id == category_id: - return c.settings - push_error("Category %s not found" % category_id) - return null +func set_setting_unchecked(key: String, value): + value = value.duplicate(true) if value is Array else value + if key in settings and typeof(settings[key]) == typeof(value) and not value is Array and settings[key] == value: return + settings[key] = value + Settings.trigger_hook(key, value) + save_settings() 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) return - if get_setting(key) != value: - settings[key].set_value(value) - save_settings() + else: set_setting_unchecked(key, value) func get_profile(key: String): if profile.has(key): @@ -391,43 +209,8 @@ func find_menu(node: Node) -> Menu: if node is Menu: return node else: return find_menu(node.get_parent()) -const NATIVE_LANGUAGE_NAMES = { - "en": "English", - "de": "Deutsch", - "fr": "Français", - "es": "Español", - "eu": "euskara", - "ja": "日本語", - "he": "עִברִית", - "tr": "Türkçe", - "fi": "suomen", - "ar": "العربية", - "zh_Hans": "中文 (简化字)", - "zh_Hant": "中文 (繁體字)", - "pl": "Polski", - "pt": "Português", -} - -func language_display(l: String): return "%s (%s)" % [NATIVE_LANGUAGE_NAMES[l], l] -func language_array() -> Array: - var lang: Array = [["system", tr("System default")]] - var to_order : Array = [] - for l in TranslationServer.get_loaded_locales(): - to_order.append([l, language_display(l)]) - to_order.append(["en", language_display("en")]) - to_order = sort_language_array(to_order) - for i in to_order: - lang.append(i) - return lang - -func sort_language_array(lang : Array) -> Array: - var sorting_array : Array = [] - var sorted_lang : Array = [] - for i in lang.size(): - sorting_array.append(lang[i][0]) - sorting_array.sort() - for shorthand in sorting_array: - for arr in lang: - if arr[0] == shorthand: - sorted_lang.append(arr) - return sorted_lang +func language_list(): + var a = TranslationServer.get_loaded_locales() + a.sort() + a.insert(0, "system") + return a diff --git a/client/makefile b/client/makefile index 2f17bee1..085ed748 100644 --- a/client/makefile +++ b/client/makefile @@ -37,4 +37,4 @@ po/locales.csv: $(LT) po/%.po: ../locale/%.ini $(LT) @mkdir -p po - $(LT) export-po $< $@ --remap-ids ../locale/en.ini + $(LT) export-po $< $@ --fallback ../locale/en.ini diff --git a/client/map/auto_setup/environment_setup.gd b/client/map/auto_setup/environment_setup.gd index 3ce8add3..c484ca1d 100644 --- a/client/map/auto_setup/environment_setup.gd +++ b/client/map/auto_setup/environment_setup.gd @@ -22,12 +22,9 @@ func set_sky(sky_name: String): environment.sky.sky_material = load("res://map/environment/presets/%s_sky.tres" % sky_name) func _ready(): - apply_settings() - Global.settings_changed.connect(apply_settings) - -func apply_settings(): - environment.ssao_enabled = Global.get_setting("ssao") - environment.sdfgi_enabled = Global.get_setting("gi") == 1 and allow_sdfgi - environment.glow_enabled = Global.get_setting("glow") + Settings.hook_changed_init("graphics.ssao", false, func (x): environment.ssao_enabled = x) + Settings.hook_changed_init("graphics.gi", false, func (x): environment.sdfgi_enabled = x == "sdfgi" and allow_sdfgi) + Settings.hook_changed_init("graphics.glow", false, func (x): environment.glow_enabled = x) + if !Global.on_vulkan(): environment.environment.tonemap_exposure = 0.5 diff --git a/client/map/auto_setup/light_setup.gd b/client/map/auto_setup/light_setup.gd index 1e256dae..7d1b0b7a 100644 --- a/client/map/auto_setup/light_setup.gd +++ b/client/map/auto_setup/light_setup.gd @@ -19,13 +19,12 @@ class_name LightSetup @export var completely_disable_light_if_shadows_disabled := false func _ready(): - apply_settings() - Global.settings_changed.connect(apply_settings) + Settings.hook_changed_init("graphics.shadows", false, apply_settings) -func apply_settings(): +func apply_settings(setting: bool): if completely_disable_light_if_shadows_disabled: - visible = Global.get_setting("shadows") - shadow_enabled = Global.get_setting("shadows") + visible = setting + shadow_enabled = setting func set_sky(sky_name: String): match sky_name: diff --git a/client/map/map.gd b/client/map/map.gd index 208665bf..ce3c6dfa 100644 --- a/client/map/map.gd +++ b/client/map/map.gd @@ -52,17 +52,18 @@ func clear_tile(pos: Vector2i): func _ready(): voxelgi_timer.connect("timeout", gi_bake) - Global.settings_changed.connect(func(): - # is not baked yet but setting is true - if Global.get_setting("gi") == 2 and not currently_baked: - gi_bake() - else: - currently_baked = false - voxelgi.data = null - ) + Settings.hook_changed("graphics.gi", false, apply_gi_setting) + +func apply_gi_setting(state): + if state == "voxelgi" and not currently_baked: + gi_bake() + else: + currently_baked = false + voxelgi.data = null + func gi_bake(): - if not Global.get_setting("gi") == 2: return + if Global.get_setting("graphics.gi") != "voxelgi": return print("Map: Rebaking VoxelGI") currently_baked = true gi_bake_blocking() diff --git a/client/map/tiles/exterior_tree.gd b/client/map/tiles/exterior_tree.gd index edc08df3..9054cbaa 100644 --- a/client/map/tiles/exterior_tree.gd +++ b/client/map/tiles/exterior_tree.gd @@ -27,7 +27,7 @@ func _init(rename: String, _neighbors: Array): var trunk: Mesh = load("res://map/tiles/tree/trunk_%d.res" % tree) var leaves: Mesh = load("res://map/tiles/tree/leaves_%d_%s.res" % [ tree, - "lq" if Global.get_setting("lq_trees") else "hq" + "lq" if Global.get_setting("graphics.lq_trees") else "hq" ]) var trunk_instance: MeshInstance3D = MeshInstance3D.new() trunk_instance.mesh = trunk diff --git a/client/map/tiles/grass.gd b/client/map/tiles/grass.gd index b33642a3..4bd3587c 100644 --- a/client/map/tiles/grass.gd +++ b/client/map/tiles/grass.gd @@ -25,7 +25,7 @@ func _init(rename: String, _neighbors: Array): var random = RandomNumberGenerator.new() random.seed = rename.hash() - for _i in Global.get_setting("grass_amount"): + for _i in Global.get_setting("graphics.grass_amount"): var g: Node3D = GRASS_SIDE.instantiate() base_mesh.add_child(g) g.position = Vector3(random.randf_range(-.5, .5), 0, random.randf_range(-.5, .5)) diff --git a/client/map/tiles/light_tile.gd b/client/map/tiles/light_tile.gd index fac3ee39..346cc3bb 100644 --- a/client/map/tiles/light_tile.gd +++ b/client/map/tiles/light_tile.gd @@ -18,12 +18,5 @@ extends Node3D @export var lights: Array[Light3D] func _ready(): - Global.settings_changed.connect(func(): - for l in lights: - update_shadows() - ) - update_shadows() - -func update_shadows(): - for l in lights: - l.shadow_enabled = Global.get_setting("shadows") + # TODO hook settings + for l in lights: l.shadow_enabled = Global.get_setting("graphics.shadows") diff --git a/client/menu/blur_setup.gd b/client/menu/blur_setup.gd index 97729074..331d1f47 100644 --- a/client/menu/blur_setup.gd +++ b/client/menu/blur_setup.gd @@ -16,8 +16,8 @@ extends Control func _ready(): - update() - Global.settings_changed.connect(update) + update(Global.get_setting("graphics.ui_blur")) + Settings.hook_changed("graphics.ui_blur", false, update) -func update(): - material.set_shader_parameter("enable_blur", Global.get_setting("ui_blur")) +func update(state): + material.set_shader_parameter("enable_blur", state) diff --git a/client/menu/character.tscn b/client/menu/character.tscn index ef0cd842..6652982c 100644 --- a/client/menu/character.tscn +++ b/client/menu/character.tscn @@ -82,7 +82,7 @@ grow_horizontal = 2 [node name="Label" type="Label" parent="VBoxContainer/top_panel/a"] layout_mode = 2 -text = "Username" +text = "c.settings.username" horizontal_alignment = 1 [node name="username" type="LineEdit" parent="VBoxContainer/top_panel/a"] @@ -144,7 +144,7 @@ offset_bottom = 22.0 grow_horizontal = 2 grow_vertical = 2 size_flags_vertical = 8 -text = "Back" +text = "c.menu.back" [node name="SceneTransition" parent="." instance=ExtResource("4_c0ocf")] visible = false diff --git a/client/menu/credits.gd b/client/menu/credits.gd index 0eccaae2..52a3b7e6 100644 --- a/client/menu/credits.gd +++ b/client/menu/credits.gd @@ -22,13 +22,13 @@ const cc_by_4 := "CC-BY 4.0" const cc_by_3 := "CC-BY 3.0" const cc0 := "CC0" var credits := [ - [tr("Models"), [ + [tr("c.credits.models"), [ ["kenney.nl", "Various Models", cc0], ["Kay Lousberg", "Kitchen tiles", cc0], ["Poly by Google", "Strawberry", cc_by_3], ["Poly by Google", "Fish", cc_by_3] ]], - [tr("Sounds"), [ + [tr("c.credits.sounds"), [ ["Dryoma", "Footstep sounds", cc_by_4], ["Koops", "Page_Turn_24.wav", cc_by_4], ["InspectorJ", "Pencil, Writing, Close, A.wav", cc_by_4], @@ -49,8 +49,8 @@ func _ready(): var text = "[center]" text += "\n\n\n[b]%s[/b]\n\n%s\n\n[b]%s[/b]\n\n\n" % [ - tr("Hurry Curry! - a game about cooking"), - tr("developed by"), + tr("c.credits.title"), + tr("c.credits.developed_by"), "\n".join(contributors) ] @@ -62,7 +62,7 @@ func _ready(): text += "[cell][left]%s[/left][/cell]" % entry[2] text += "[/table]\n\n\n" - text += "\n[b]%s[/b]\n\n\n[/center]" % tr("Thank You For Playing") + text += "\n[b]%s[/b]\n\n\n[/center]" % tr("c.credits.thanks") label.text = text diff --git a/client/menu/entry.gd b/client/menu/entry.gd index 5ca6dacb..456709ed 100644 --- a/client/menu/entry.gd +++ b/client/menu/entry.gd @@ -18,7 +18,7 @@ extends Menu func _ready(): super() - if not Global.get_setting("setup_complete"): + if not Global.get_setting("gameplay.setup_complete"): await submenu("res://menu/setup.tscn") else: await submenu("res://menu/main.tscn") diff --git a/client/menu/game.gd b/client/menu/game.gd index f50a43a6..a2eb858c 100644 --- a/client/menu/game.gd +++ b/client/menu/game.gd @@ -43,7 +43,7 @@ func _menu_cover(state): game.camera.update_disable_input() func _process(_delta): - if Global.get_setting("debug_info"): + if Global.get_setting("graphics.debug_info"): debug_label.show() debug_label.text = "%d FPS\nDriver: %s" % [Engine.get_frames_per_second(), ProjectSettings.get_setting("rendering/rendering_device/driver")] else: debug_label.hide() diff --git a/client/menu/ingame.tscn b/client/menu/ingame.tscn index ce42bbdd..55678847 100644 --- a/client/menu/ingame.tscn +++ b/client/menu/ingame.tscn @@ -94,22 +94,22 @@ layout_mode = 2 [node name="Resume" type="Button" parent="Side/Margin/Options"] layout_mode = 2 -text = "Resume" +text = "c.menu.ingame.resume" alignment = 0 [node name="Leave" type="Button" parent="Side/Margin/Options"] layout_mode = 2 -text = "Join Game" +text = "c.menu.ingame.join" alignment = 0 [node name="Lobby" type="Button" parent="Side/Margin/Options"] layout_mode = 2 -text = "Cancel game" +text = "c.menu.ingame.cancel" alignment = 0 [node name="Reconnect" type="Button" parent="Side/Margin/Options"] layout_mode = 2 -text = "Reconnect" +text = "c.menu.ingame.reconnect" alignment = 0 [node name="Spacer2" type="Control" parent="Side/Margin/Options"] @@ -118,7 +118,7 @@ layout_mode = 2 [node name="Settings" type="Button" parent="Side/Margin/Options"] layout_mode = 2 -text = "Settings" +text = "c.menu.settings" alignment = 0 [node name="Spacer3" type="Control" parent="Side/Margin/Options"] @@ -127,12 +127,12 @@ layout_mode = 2 [node name="MainMenu" type="Button" parent="Side/Margin/Options"] layout_mode = 2 -text = "Main menu" +text = "c.menu.ingame.main_menu" alignment = 0 [node name="Quit" type="Button" parent="Side/Margin/Options"] layout_mode = 2 -text = "Quit game" +text = "c.menu.quit" alignment = 0 [connection signal="pressed" from="Side/Margin/Options/Resume" to="." method="_on_resume_pressed"] diff --git a/client/menu/lobby.tscn b/client/menu/lobby.tscn index 05b6507c..d1fe2233 100644 --- a/client/menu/lobby.tscn +++ b/client/menu/lobby.tscn @@ -124,7 +124,7 @@ layout_mode = 2 [node name="EnableBots" type="CheckButton" parent="HBoxContainer/Bottom/MarginContainer/VBoxContainer/Bots"] layout_mode = 2 -text = "Enable bots" +text = "c.menu.lobby.enable_bots" [node name="ScrollContainerCustom" type="ScrollContainer" parent="HBoxContainer/Bottom/MarginContainer/VBoxContainer/Bots"] visible = false @@ -145,7 +145,7 @@ alignment = 1 [node name="JoinSpectate" parent="HBoxContainer/Bottom/MarginContainer/VBoxContainer/VBoxContainer" instance=ExtResource("7_t6mox")] custom_minimum_size = Vector2(148, 0) layout_mode = 2 -text = "Spectate" +text = "c.menu.ingame.spectate" expand_icon = true controller_texture = ExtResource("11_5uugf") press_action = "join_spectate" @@ -153,7 +153,7 @@ press_action = "join_spectate" [node name="Start" parent="HBoxContainer/Bottom/MarginContainer/VBoxContainer/VBoxContainer" instance=ExtResource("7_t6mox")] custom_minimum_size = Vector2(148, 0) layout_mode = 2 -text = "Start game" +text = "c.menu.lobby.start_game" expand_icon = true controller_texture = ExtResource("9_q14bw") press_action = "start_game" diff --git a/client/menu/main.tscn b/client/menu/main.tscn index 94726109..26decd2b 100644 --- a/client/menu/main.tscn +++ b/client/menu/main.tscn @@ -63,27 +63,27 @@ layout_mode = 2 [node name="play" type="Button" parent="side/margin/options/first"] layout_mode = 2 -text = "Play" +text = "c.menu.play" alignment = 0 [node name="change_character" type="Button" parent="side/margin/options/first"] layout_mode = 2 -text = "My Chef" +text = "c.menu.my_chef" alignment = 0 [node name="settings" type="Button" parent="side/margin/options/first"] layout_mode = 2 -text = "Settings" +text = "c.menu.settings" alignment = 0 [node name="credits" type="Button" parent="side/margin/options/first"] layout_mode = 2 -text = "Credits" +text = "c.menu.credits" alignment = 0 [node name="quit" type="Button" parent="side/margin/options/first"] layout_mode = 2 -text = "Quit" +text = "c.menu.quit" alignment = 0 [connection signal="pressed" from="side/margin/options/first/play" to="." method="_on_play_pressed"] diff --git a/client/menu/overlay.tscn b/client/menu/overlay.tscn index e28013db..0947e308 100644 --- a/client/menu/overlay.tscn +++ b/client/menu/overlay.tscn @@ -67,16 +67,16 @@ layout_mode = 2 theme_override_colors/font_color = Color(0, 0, 0, 1) theme_override_fonts/font = ExtResource("3_u54fv") theme_override_font_sizes/font_size = 25 -text = "Completed" +text = "c.score.completed" [node name="Spacer" type="Control" parent="Score/Paper/Margin/Lines/Line1"] layout_mode = 2 size_flags_horizontal = 3 [node name="Completed" type="Label" parent="Score/Paper/Margin/Lines/Line1"] +auto_translate_mode = 2 custom_minimum_size = Vector2(100, 0) layout_mode = 2 -auto_translate = false theme_override_colors/font_color = Color(0, 0.278431, 0, 1) theme_override_fonts/font = ExtResource("3_u54fv") theme_override_font_sizes/font_size = 35 @@ -91,16 +91,16 @@ layout_mode = 2 theme_override_colors/font_color = Color(0, 0, 0, 1) theme_override_fonts/font = ExtResource("3_u54fv") theme_override_font_sizes/font_size = 25 -text = "Failed" +text = "c.score.failed" [node name="Spacer" type="Control" parent="Score/Paper/Margin/Lines/Line2"] layout_mode = 2 size_flags_horizontal = 3 [node name="Failed" type="Label" parent="Score/Paper/Margin/Lines/Line2"] +auto_translate_mode = 2 custom_minimum_size = Vector2(100, 0) layout_mode = 2 -auto_translate = false theme_override_colors/font_color = Color(0.505882, 0, 0, 1) theme_override_fonts/font = ExtResource("3_u54fv") theme_override_font_sizes/font_size = 35 @@ -115,16 +115,16 @@ layout_mode = 2 theme_override_colors/font_color = Color(0, 0, 0, 1) theme_override_fonts/font = ExtResource("3_u54fv") theme_override_font_sizes/font_size = 35 -text = "Points" +text = "c.score.points" [node name="Spacer" type="Control" parent="Score/Paper/Margin/Lines/Line3"] layout_mode = 2 size_flags_horizontal = 3 [node name="Points" type="Label" parent="Score/Paper/Margin/Lines/Line3"] +auto_translate_mode = 2 custom_minimum_size = Vector2(100, 0) layout_mode = 2 -auto_translate = false theme_override_colors/font_color = Color(0, 0, 0, 1) theme_override_fonts/font = ExtResource("3_u54fv") theme_override_font_sizes/font_size = 45 @@ -156,9 +156,9 @@ texture = ExtResource("3_oum5g") layout_mode = 0 [node name="Seconds" type="Label" parent="Time/Paper/Line"] +auto_translate_mode = 2 custom_minimum_size = Vector2(100, 0) layout_mode = 2 -auto_translate = false theme_override_colors/font_color = Color(0, 0, 0, 1) theme_override_fonts/font = ExtResource("3_u54fv") theme_override_font_sizes/font_size = 45 @@ -166,8 +166,8 @@ text = "300" horizontal_alignment = 2 [node name="Point" type="Label" parent="Time/Paper/Line"] +auto_translate_mode = 2 layout_mode = 2 -auto_translate = false theme_override_colors/font_color = Color(0, 0, 0, 1) theme_override_fonts/font = ExtResource("3_u54fv") theme_override_font_sizes/font_size = 45 @@ -175,8 +175,8 @@ text = "." horizontal_alignment = 1 [node name="Decimals" type="Label" parent="Time/Paper/Line"] +auto_translate_mode = 2 layout_mode = 2 -auto_translate = false theme_override_colors/font_color = Color(0, 0, 0, 1) theme_override_fonts/font = ExtResource("3_u54fv") theme_override_font_sizes/font_size = 45 diff --git a/client/menu/play.tscn b/client/menu/play.tscn index 959ca760..b91f7bda 100644 --- a/client/menu/play.tscn +++ b/client/menu/play.tscn @@ -59,7 +59,7 @@ layout_mode = 2 [node name="quick_connect" type="Button" parent="side/margin/options/second"] layout_mode = 2 -text = "Quick Connect" +text = "c.menu.play.quick_connect" alignment = 0 [node name="connect" type="HBoxContainer" parent="side/margin/options/second"] @@ -73,7 +73,7 @@ placeholder_text = "wss://example.org" [node name="connect" type="Button" parent="side/margin/options/second/connect"] layout_mode = 2 -text = "Connect" +text = "c.menu.play.connect" [node name="server" type="HBoxContainer" parent="side/margin/options/second"] layout_mode = 2 @@ -81,12 +81,12 @@ layout_mode = 2 [node name="control" type="Button" parent="side/margin/options/second/server"] layout_mode = 2 size_flags_horizontal = 3 -text = "Server" +text = "c.menu.play.server" alignment = 0 [node name="connect" type="Button" parent="side/margin/options/second/server"] layout_mode = 2 -text = "Connect" +text = "c.menu.play.connect" [node name="spacer2" type="Control" parent="side/margin/options/second"] custom_minimum_size = Vector2(0, 10) @@ -94,7 +94,7 @@ layout_mode = 2 [node name="back" type="Button" parent="side/margin/options/second"] layout_mode = 2 -text = "Back" +text = "c.menu.back" alignment = 0 [connection signal="pressed" from="side/margin/options/second/quick_connect" to="." method="_on_quick_connect_pressed"] diff --git a/client/menu/popup_message.gd b/client/menu/popup_message.gd index 90e86faa..1464e20c 100644 --- a/client/menu/popup_message.gd +++ b/client/menu/popup_message.gd @@ -109,24 +109,24 @@ func _input(_event): Global.set_hint("has_reset", true) func _on_boost_timeout(): - if not Global.get_hint("has_boosted") and not Global.get_setting("touch_controls"): + if not Global.get_hint("has_boosted") and not Global.get_setting("ui.touch_controls"): display_hint_msg(tr("Press %s to boost") % display_keybind(tr("SHIFT"), "B")) func _on_move_timeout(): - if not Global.get_hint("has_moved") and not Global.get_setting("touch_controls"): + if not Global.get_hint("has_moved") and not Global.get_setting("ui.touch_controls"): display_hint_msg(tr("Use %s to move") % display_keybind("WASD", tr("left stick"))) func _on_interact_timeout(): - if not Global.get_hint("has_interacted") and not Global.get_setting("touch_controls"): + if not Global.get_hint("has_interacted") and not Global.get_setting("ui.touch_controls"): var keybind = display_keybind(tr("SPACE"), "A") display_hint_msg(tr("Press %s to pick up items and hold %s to interact with tools") % [keybind, keybind]) func _on_reset_timeout(): - if not Global.get_hint("has_reset") and not Global.get_setting("touch_controls"): + if not Global.get_hint("has_reset") and not Global.get_setting("ui.touch_controls"): display_hint_msg(tr("Press %s to reset the camera view") % display_keybind("R", "Y")) func _on_zoom_timeout(): - if not Global.get_hint("has_zoomed") and not Global.get_setting("touch_controls"): + if not Global.get_hint("has_zoomed") and not Global.get_setting("ui.touch_controls"): display_hint_msg(tr("Use %s to zoom in/out") % display_keybind(tr("PageUp/PageDown"), "LT/RT")) func display_keybind(keyboard: String, joypad: String, touch = null) -> String: @@ -143,11 +143,11 @@ func any_action_just_pressed(actions: Array) -> bool: return false func _on_rotate_camera_timeout(): - if not Global.get_hint("has_rotated") and not Global.get_setting("touch_controls"): + if not Global.get_hint("has_rotated") and not Global.get_setting("ui.touch_controls"): display_hint_msg(tr("Use %s to rotate the camera view") % display_keybind(tr("arrow keys"), tr("right stick"))) func _on_nametags_timeout(): - if not Global.get_hint("has_seen_nametags") and not Global.get_setting("usernames"): + if not Global.get_hint("has_seen_nametags") and not Global.get_setting("graphics.usernames"): Global.set_hint("has_seen_nametags", true) display_hint_msg(tr("Username tags can be enabled/disabled in the settings")) diff --git a/client/menu/settings.gd b/client/menu/settings.gd index eb143900..f035f712 100644 --- a/client/menu/settings.gd +++ b/client/menu/settings.gd @@ -16,52 +16,17 @@ # extends Menu -const SETTINGS_ROW_SCENE = preload("res://menu/settings/settings_row.tscn") - -@onready var settings_tabs: TabContainer = $OuterGap/Panel/InnerGap/VBoxContainer/TabContainer +@onready var container = $OuterGap/Panel/InnerGap/VBoxContainer func _ready(): super() - update_rows() + var row = Global.settings_tree.create_row() + container.add_child(row) + container.move_child(row, 1) func _on_back_pressed(): exit() func exit(): Global.save_settings() - Global.apply_settings() super() - -func update_rows(fix_focus = false): - for c in settings_tabs.get_children(): - c.queue_free() - - for category: SettingsCategory in Global.default_settings: - var category_settings = category.settings - var scroll := ScrollContainerCustom.new() - var options := VBoxContainer.new() - scroll.name = category.name - scroll.size_flags_horizontal = Control.SIZE_EXPAND_FILL - settings_tabs.add_child(scroll) - options.size_flags_horizontal = Control.SIZE_EXPAND_FILL - scroll.add_child(options) - - var category_presets = category.presets - if category_presets != null: - for i: Preset in category_presets: - var row: SettingsRow = SETTINGS_ROW_SCENE.instantiate() - options.add_child(row) - row.label.text = i.label - row.reset_button.visible = false - for b in i.buttons(): - row.value_parent.add_child(b) - - for k: String in category_settings.keys(): - var row: SettingsRow = Global.settings[k].get_row() - options.add_child(row) - - if fix_focus: - pass - # TODO: Not implemented! - # await get_tree().process_frame - # Global.focus_first(self) diff --git a/client/menu/settings.tscn b/client/menu/settings.tscn index 9f523f7c..0a55e222 100644 --- a/client/menu/settings.tscn +++ b/client/menu/settings.tscn @@ -1,12 +1,10 @@ -[gd_scene load_steps=6 format=3 uid="uid://8ic77jmadadj"] +[gd_scene load_steps=5 format=3 uid="uid://8ic77jmadadj"] [ext_resource type="Theme" uid="uid://b0qmvo504e457" path="res://menu/theme/theme.tres" id="1_foq3a"] [ext_resource type="Script" path="res://menu/settings.gd" id="2_3hgm8"] [ext_resource type="Material" uid="uid://beea1pc5nt67r" path="res://menu/theme/dark_blur_material.tres" id="3_8nykw"] [ext_resource type="Script" path="res://menu/blur_setup.gd" id="4_v6q3y"] -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_85urc"] - [node name="SettingsMenu" type="Control"] layout_mode = 3 anchors_preset = 15 @@ -52,11 +50,6 @@ size_flags_horizontal = 0 theme_override_font_sizes/font_size = 36 text = "Settings" -[node name="TabContainer" type="TabContainer" parent="OuterGap/Panel/InnerGap/VBoxContainer"] -layout_mode = 2 -size_flags_vertical = 3 -theme_override_styles/panel = SubResource("StyleBoxEmpty_85urc") - [node name="Back" type="Button" parent="OuterGap/Panel/InnerGap/VBoxContainer"] layout_mode = 2 size_flags_vertical = 8 diff --git a/client/menu/settings/dropdown_setting.gd b/client/menu/settings/dropdown_setting.gd index e57fc40f..3d6b7c80 100644 --- a/client/menu/settings/dropdown_setting.gd +++ b/client/menu/settings/dropdown_setting.gd @@ -18,23 +18,14 @@ extends GameSetting var options: Array -func _init(new_description: String, new_preset: int, new_options: Array): - assert(new_preset < new_options.size()) - super(new_description, new_preset) +func _init(new_id: String, new_default, new_options: Array): + super(new_id, new_default) options = new_options -func _update_row(): - super() +func create_row(): + var row = super() row.value_node = OptionButton.new() - for i in options: - row.value_node.add_item(i) - row.value_node.select(_value) - -func fetch_setting(): - if row != null: - _value = row.value_node.selected - -func set_value(v): - super(v) - if row != null: - row.value_node.selected = _value + for i in options: row.value_node.add_item(tr(nskey + "." + i)) + Settings.hook_changed_init(key, true, func (value): row.value_node.select(options.find(value))) + row.value_node.item_selected.connect(func(item): Global.set_setting(key, options[item])) + return row diff --git a/client/menu/settings/game_setting.gd b/client/menu/settings/game_setting.gd index afe1fe65..a98d5abb 100644 --- a/client/menu/settings/game_setting.gd +++ b/client/menu/settings/game_setting.gd @@ -16,39 +16,27 @@ class_name GameSetting extends Object -var preset -var _value -var description: String +var default +var key: String +var nskey: String -var row: SettingsRow +func _init(new_id: String, new_default = null): + default = new_default + key = new_id -func _init(new_description: String, new_preset): - preset = new_preset - set_value(new_preset) - description = new_description +func set_parent(parent: GameSetting): + if parent != null: key = parent.key + "." + key + nskey = "c.settings." + key -func reset(): - set_value(preset) - -func get_row() -> SettingsRow: - _update_row() +func create_row(): + var row = preload("res://menu/settings/settings_row.tscn").instantiate() + row.description = tr(nskey) + row.reset.connect(func(): Global.set_setting(key, default)) return row -func _update_row(): - if row != null: - row.queue_free() - row = preload("res://menu/settings/settings_row.tscn").instantiate() - row.description = description - row.reset.connect(reset) - -func fetch_setting(): - pass - -func get_value(): - return _value - -func set_value(v): - if v is Array: - _value = v.duplicate(true) - else: - _value = v +func check(): + if default != null: + if not key in Global.settings: + Global.set_setting_unchecked(key, default) + if typeof(default) != typeof(Global.settings[key]): + Global.set_setting_unchecked(key, default) diff --git a/client/menu/settings/input/input_manager.gd b/client/menu/settings/input/input_manager.gd index 96cdca09..d216884b 100644 --- a/client/menu/settings/input/input_manager.gd +++ b/client/menu/settings/input/input_manager.gd @@ -1,5 +1,6 @@ # Hurry Curry! - a game about cooking # Copyright 2024 tpart +# Copyright 2024 metamuffin # # 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 @@ -15,32 +16,6 @@ # extends Node -var action_descriptions = { - "forwards": tr("Move forwards"), - "backwards": tr("Move backwards"), - "left": tr("Move left"), - "right": tr("Move right"), - "rotate_left": tr("Rotate camera to the left"), - "rotate_right": tr("Rotate camera to the right"), - "rotate_up": tr("Rotate camera upwards"), - "rotate_down": tr("Rotate camera downwards"), - "interact": tr("Interact", "Interacting with items, etc."), - "boost": tr("Boost movement"), - "zoom_in": tr("Zoom in"), - "zoom_out": tr("Zoom out"), - "chat": tr("Toggle chat", "Toggle chat on or off"), - "reset": tr("Reset camera view"), - "fullscreen": tr("Toggle fullscreen"), - "previous": tr("Previous"), - "next": tr("Next"), - "start_game": tr("Start game"), - "join_spectate": tr("Join / Spectate"), - "zoom_in_discrete": tr("Zoom in (discrete)"), - "zoom_out_discrete": tr("Zoom out (discrete)"), - "scroll_down": tr("Scroll down"), - "scroll_up": tr("Scroll up"), -} - var default_input_map = {} var input_map @@ -56,19 +31,12 @@ func get_input_map() -> Dictionary: kb[a] = input_events return kb -func input_map_to_settings_dictionary(map: Dictionary) -> Dictionary: - var settings_dict := {} +func input_map_to_settings(map: Dictionary) -> Array: + var entries := [] for k in map.keys(): var events = map[k] - settings_dict[k] = InputSetting.new(action_descriptions[k] if action_descriptions.has(k) else k, events) - return settings_dict - -func settings_dictionary_to_input_map(settings: Dictionary) -> Dictionary: - var map := {} - for k in settings.keys(): - var setting: InputSetting = settings[k] - map[k] = setting.get_value() - return map + entries.append(InputSetting.new(k, events)) + return entries func change_input_map_action(action_name: String, events: Array, save: bool = true): if !InputMap.has_action(action_name): diff --git a/client/menu/settings/input/input_setting.gd b/client/menu/settings/input/input_setting.gd index eec68bdc..7388af78 100644 --- a/client/menu/settings/input/input_setting.gd +++ b/client/menu/settings/input/input_setting.gd @@ -1,5 +1,6 @@ # Hurry Curry! - a game about cooking # Copyright 2024 tpart +# Copyright 2024 metamuffin # # 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 @@ -18,17 +19,9 @@ extends GameSetting const INPUT_VALUE_NODE_SCENE = preload("res://menu/settings/input/input_value_node.tscn") -func _update_row(): - super() +func create_row(): + var row = super() row.value_node = INPUT_VALUE_NODE_SCENE.instantiate() - row.value_node.value = _value - -func fetch_setting(): - if row != null: - _value = row.value_node.value - -func set_value(v): - super(v) - if row != null: - row.value_node.value = _value - row.value_node.update() + Settings.hook_changed_init(key, true, func(value): row.value_node.value = value) + row.value_node.changed.connect(func(): Global.set_setting(key, row.value_node.value)) + return row diff --git a/client/menu/settings/input/input_value_node.gd b/client/menu/settings/input/input_value_node.gd index 125a946b..9f89416b 100644 --- a/client/menu/settings/input/input_value_node.gd +++ b/client/menu/settings/input/input_value_node.gd @@ -1,5 +1,6 @@ # Hurry Curry! - a game about cooking # Copyright 2024 tpart +# Copyright 2024 metamuffin # # 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 @@ -19,6 +20,8 @@ class_name InputValueNode var value: Array[InputEvent] = [] var listening := false +signal changed() + @onready var actions_container: VBoxContainer = $ActionsContainer @onready var add_button: Button = $Add @onready var add_text = add_button.text @@ -55,6 +58,7 @@ func update(fix_focus: bool = false): func erase_event(e: InputEvent): value.erase(e) update(true) + changed.emit() func _input(e: InputEvent): if listening: @@ -66,6 +70,7 @@ func _input(e: InputEvent): value.append(e) _on_add_pressed() update() + changed.emit() func events_equal(e1: InputEvent, e2: InputEvent) -> bool: if e1 is InputEventKey and e2 is InputEventKey: diff --git a/client/menu/settings/preset.gd b/client/menu/settings/preset_row.gd index bb13b570..af6cfe0e 100644 --- a/client/menu/settings/preset.gd +++ b/client/menu/settings/preset_row.gd @@ -1,5 +1,6 @@ # Hurry Curry! - a game about cooking # Copyright 2024 nokoe +# Copyright 2024 metamuffin # # 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 @@ -13,27 +14,32 @@ # 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/>. # -class_name Preset -extends Object +class_name PresetRow +extends GameSetting -var label: String var options: Dictionary +var arr: Array[Button] -func _init(name_: String, options_: Dictionary): - label = name_ +func _init(id: String, options_: Dictionary): + super(id) options = options_ +var prefix = "" +func set_parent(parent): + super(parent) + if parent != null: prefix = parent.key + func apply(preset_name: String): - var preset: Dictionary = options[preset_name] + var preset = options[preset_name] for i in preset.keys(): - var setting_name: String = i - Global.set_setting(setting_name, preset[setting_name]) + Global.set_setting(prefix + "." + i, preset[i]) -func buttons() -> Array[Button]: - var arr: Array[Button] = [] +func create_row(): + var row = super() + row.value_node = HBoxContainer.new() for i in options.keys(): var button := Button.new() button.pressed.connect(apply.bind(i)) - button.text = i - arr.push_back(button) - return arr + button.text = tr(nskey + "." + i) + row.value_node.add_child(button) + return row diff --git a/client/menu/settings/range_setting.gd b/client/menu/settings/range_setting.gd index d13e2262..b119a205 100644 --- a/client/menu/settings/range_setting.gd +++ b/client/menu/settings/range_setting.gd @@ -1,5 +1,6 @@ # Hurry Curry! - a game about cooking # Copyright 2024 nokoe +# Copyright 2024 metamuffin # # 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 @@ -21,27 +22,20 @@ var max_value: float var tick_count var smooth: bool -func _init(new_description: String, new_preset: float, new_min_value: float, new_max_value: float, new_smooth: bool = true, new_tick_count = null): - super(new_description, new_preset) +func _init(new_id: String, new_default: float, new_min_value: float, new_max_value: float, new_smooth: bool = true, new_tick_count = null): + super(new_id, new_default) min_value = new_min_value max_value = new_max_value tick_count = new_tick_count smooth = new_smooth -func _update_row(): - super() +func create_row(): + var row = super() row.value_node = HSlider.new() row.value_node.min_value = min_value row.value_node.max_value = max_value row.value_node.tick_count = abs(max_value - min_value) if tick_count == null else tick_count row.value_node.step = 0 if smooth else (1 if tick_count == null else abs(max_value - min_value) / (tick_count - 1)) - row.value_node.value = _value - -func fetch_setting(): - if row != null: - _value = row.value_node.value - -func set_value(v): - super(v) - if row != null: - row.value_node.value = _value + Settings.hook_changed_init(key, true, func(value): row.value_node.value = value) + row.value_node.value_changed.connect(func(value): Global.set_setting(key, value)) + return row diff --git a/client/menu/settings/settings_category.gd b/client/menu/settings/settings_category.gd index c4601429..b5d02d8f 100644 --- a/client/menu/settings/settings_category.gd +++ b/client/menu/settings/settings_category.gd @@ -14,15 +14,32 @@ # along with this program. If not, see <https://www.gnu.org/licenses/>. # class_name SettingsCategory -extends Object +extends GameSetting -var name: String -var id: String -var settings: Dictionary # Dictionary[String, GameSetting] -var presets # Array[Preset] | null +var settings: Array # Dictionary[String, GameSetting] -func _init(new_name: String, new_id: String, new_settings: Dictionary, new_presets = null): - name = new_name - id = new_id +var options: VBoxContainer + +func _init(new_id: String, new_settings: Array): + super(new_id) settings = new_settings - presets = new_presets + +func set_parent(parent: GameSetting): + super(parent) + for c in settings: + c.set_parent(self) + +func create_row(): + var row = ScrollContainerCustom.new() + var options = VBoxContainer.new() + row.name = tr(nskey) + row.size_flags_horizontal = Control.SIZE_EXPAND_FILL + options.size_flags_horizontal = Control.SIZE_EXPAND_FILL + row.add_child(options) + + for r in settings: options.add_child(r.create_row()) + return row + +func check(): + for c in settings: + c.check() diff --git a/client/menu/settings/settings_root.gd b/client/menu/settings/settings_root.gd new file mode 100644 index 00000000..1beb0d9b --- /dev/null +++ b/client/menu/settings/settings_root.gd @@ -0,0 +1,34 @@ +# Hurry Curry! - a game about cooking +# Copyright 2024 metamuffin +# +# 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 GameSetting +class_name SettingsRoot + +var children: Array +func _init(new_children: Array): + super("root") + children = new_children + for c in children: + c.set_parent(null) + +func create_row(): + var row = TabContainer.new() + row.size_flags_vertical = Control.SIZE_EXPAND_FILL + for r in children: row.add_child(r.create_row()) + return row + +func check(): + for c in children: + c.check() diff --git a/client/menu/settings/settings_row.gd b/client/menu/settings/settings_row.gd index f5621d78..68340918 100644 --- a/client/menu/settings/settings_row.gd +++ b/client/menu/settings/settings_row.gd @@ -1,5 +1,6 @@ # Hurry Curry! - a game about cooking # Copyright 2024 nokoe +# Copyright 2024 metamuffin # # 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 diff --git a/client/menu/settings/text_setting.gd b/client/menu/settings/text_setting.gd index faafa610..dc9352ab 100644 --- a/client/menu/settings/text_setting.gd +++ b/client/menu/settings/text_setting.gd @@ -1,5 +1,6 @@ # Hurry Curry! - a game about cooking # Copyright 2024 nokoe +# Copyright 2024 metamuffin # # 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 @@ -18,21 +19,14 @@ extends GameSetting var placeholder: String -func _init(new_description: String, new_preset: String, new_placeholder: String = ""): - super(new_description, new_preset) +func _init(new_id: String, new_default: String, new_placeholder: String = ""): + super(new_id, new_default) placeholder = new_placeholder -func _update_row(): - super() +func create_row(): + var row = super() row.value_node = LineEdit.new() - row.value_node.text = _value row.value_node.placeholder_text = placeholder - -func fetch_setting(): - if row != null: - _value = row.value_node.text - -func set_value(v): - super(v) - if row != null: - row.value_node.text = _value + row.value_node.text_changed.connect(func(text): Global.set_setting(key, text)) + Settings.hook_changed_init(key, true, func(text): row.value_node.text = text) + return row diff --git a/client/menu/settings/toggle_setting.gd b/client/menu/settings/toggle_setting.gd index 6d4150d4..8e0c030c 100644 --- a/client/menu/settings/toggle_setting.gd +++ b/client/menu/settings/toggle_setting.gd @@ -1,5 +1,6 @@ # Hurry Curry! - a game about cooking # Copyright 2024 nokoe +# Copyright 2024 metamuffin # # 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 @@ -16,19 +17,12 @@ class_name ToggleSetting extends GameSetting -func _init(new_description: String, new_preset: bool): - super(new_description, new_preset) +func _init(new_id: String, new_default: bool): + super(new_id, new_default) -func fetch_setting(): - if row != null: - _value = row.value_node.button_pressed - -func _update_row(): - super() +func create_row(): + var row = super() row.value_node = CheckButton.new() - row.value_node.button_pressed = _value - -func set_value(v): - super(v) - if row != null: - row.value_node.button_pressed = _value + row.value_node.pressed.connect(func(): Global.set_setting(key, row.value_node.button_pressed)) + Settings.hook_changed_init(key, true, func(value): row.value_node.button_pressed = value) + return row diff --git a/client/menu/setup.gd b/client/menu/setup.gd index 6c3c90cd..16944c7c 100644 --- a/client/menu/setup.gd +++ b/client/menu/setup.gd @@ -73,5 +73,5 @@ func _on_sign_pressed(): Global.set_profile("username", username.text) Global.set_profile("character", character) - Global.set_setting("setup_complete", true) + Global.set_setting("gameplay.setup_complete", true) replace_menu("res://menu/main.tscn") diff --git a/client/menu/setup.tscn b/client/menu/setup.tscn index 9d8e4975..53c4565e 100644 --- a/client/menu/setup.tscn +++ b/client/menu/setup.tscn @@ -209,8 +209,8 @@ layout_mode = 2 [node name="LineEdit" type="LineEdit" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/PositionEntry"] custom_minimum_size = Vector2(300, 30) layout_mode = 2 -theme_override_colors/font_color = Color(0.458824, 0, 0, 1) theme_override_colors/font_uneditable_color = Color(0.458824, 0, 0, 1) +theme_override_colors/font_color = Color(0.458824, 0, 0, 1) editable = false [node name="LineEdit2" type="Label" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/PositionEntry/LineEdit"] diff --git a/client/menu/warning_popup.gd b/client/menu/warning_popup.gd index 20741834..fd23a30a 100644 --- a/client/menu/warning_popup.gd +++ b/client/menu/warning_popup.gd @@ -2,7 +2,6 @@ extends Menu class_name WarningPopup func _ready(): - print(self.data) $CenterContainer/Panel/MarginContainer/VBoxContainer/Message.text = self.data func _on_accept_pressed(): diff --git a/client/multiplayer.gd b/client/multiplayer.gd index d8c6c1ff..168e62e6 100644 --- a/client/multiplayer.gd +++ b/client/multiplayer.gd @@ -85,10 +85,8 @@ func _process(_delta): handle_packet(socket.get_packet()) elif state == WebSocketPeer.STATE_CLOSED: var code = socket.get_close_code() - var reason = socket.get_close_reason() if code == socket.STATE_CLOSED else tr("unavailable", "The reason for the websocket closing is unavailable") - connection_closed.emit( - tr("WebSocket closed with code: %d, reason %s. Clean: %s") % [code, reason, code != -1] - ) + var reason = socket.get_close_reason() if code == socket.STATE_CLOSED else tr("c.error.websocket.unavailable") + connection_closed.emit(tr("c.error.websocket") % [code, reason, code != -1]) self.queue_free() func handle_packet(bytes: PackedByteArray): @@ -108,7 +106,7 @@ func handle_packet(bytes: PackedByteArray): if major != VERSION_MAJOR and minor >= VERSION_MINOR: socket.close() connected = false - connection_closed.emit(tr("Server and client versions do not match. Server: %d.%d, Client: %d.%d.\nAre you sure the game is up to date?") % [major, minor, VERSION_MAJOR, VERSION_MINOR]) + connection_closed.emit(tr("c.error.version_mismatch") % [major, minor, VERSION_MAJOR, VERSION_MINOR]) "data": var item_names = decoded["data"]["item_names"] var tile_names = decoded["data"]["tile_names"] diff --git a/client/player/controllable_player.gd b/client/player/controllable_player.gd index 81315630..142d5f47 100644 --- a/client/player/controllable_player.gd +++ b/client/player/controllable_player.gd @@ -60,7 +60,7 @@ func _process(delta): func _process_movement(delta): var input = Input.get_vector("left", "right", "forwards", "backwards") if is_input_enabled() else Vector2.ZERO - var boost = Input.is_action_pressed("boost") or (Global.get_setting("latch_boost") and boosting) + var boost = Input.is_action_pressed("boost") or (Global.get_setting("gameplay.latch_boost") and boosting) input = input.rotated( - game.camera.angle_target) if Input.is_action_pressed("interact") or Input.is_action_just_released("interact"): input *= 0 diff --git a/client/player/follow_camera.gd b/client/player/follow_camera.gd index 516b1ba1..88b6cad4 100644 --- a/client/player/follow_camera.gd +++ b/client/player/follow_camera.gd @@ -71,12 +71,12 @@ func reset(): func follow(delta): if not _disable_input: angle_target += Input.get_axis("rotate_left", "rotate_right") * ( - ROTATE_SPEED * delta * (-1 if Global.get_setting("invert_camera") else 1) + ROTATE_SPEED * delta * (-1 if Global.get_setting("gameplay.invert_camera") else 1) ) angle = G.interpolate_angle(angle, angle_target, delta * ROTATE_WEIGHT) if not _disable_input: angle_up_target += Input.get_axis("rotate_down", "rotate_up") * ( - ROTATE_UP_SPEED * delta * (-1 if Global.get_setting("invert_camera") else 1) + ROTATE_UP_SPEED * delta * (-1 if Global.get_setting("gameplay.invert_camera") else 1) ) angle_up_target = clamp(angle_up_target, ANGLE_UP_MIN, ANGLE_UP_MAX) angle_up = G.interpolate_angle(angle_up, angle_up_target, delta * ROTATE_UP_WEIGHT) @@ -87,7 +87,7 @@ func follow(delta): new_transform.origin = target.position + offset new_transform = new_transform.looking_at(target.position) - if Global.get_setting("interpolate_camera_rotation"): + if Global.get_setting("gameplay.interpolate_camera_rotation"): transform.basis = Basis.from_euler(Vector3( G.interpolate_angle(transform.basis.get_euler().x, new_transform.basis.get_euler().x, delta * LOOK_WEIGHT), G.interpolate_angle(transform.basis.get_euler().y, new_transform.basis.get_euler().y, delta * LOOK_WEIGHT), diff --git a/client/player/player.gd b/client/player/player.gd index d5837d47..f418337b 100644 --- a/client/player/player.gd +++ b/client/player/player.gd @@ -77,18 +77,17 @@ func _ready(): character.select_hairstyle(character_idx) clear_timer.timeout.connect(clear_message) - update_username_tag() - Global.settings_changed.connect(update_username_tag) + Settings.hook_changed_init("gameplay.usernames", false, update_username_tag) func update_position(new_position: Vector2, new_rotation: float, new_boosting: bool): position_ = new_position rotation_ = new_rotation boosting = new_boosting -func update_username_tag(): +func update_username_tag(state): var tag: Label3D = character.username_tag tag.text = username - tag.visible = Global.get_setting("usernames") + tag.visible = state func set_item(i: Item): i.owned_by = hand_base diff --git a/client/server.gd b/client/server.gd index 3529e7e4..40d87672 100644 --- a/client/server.gd +++ b/client/server.gd @@ -85,18 +85,16 @@ func _server_exec(): sem.post() func get_server_path() -> String: - var path: String = Global.get_setting("server_binary") + var path: String = Global.get_setting("other.server_binary") if path != "": return path else: return "hurrycurry-server" func get_server_data(): - var path: String = Global.get_setting("server_data") - if path != "": - return path - else: - return null + var path: String = Global.get_setting("other.server_data") + if path != "": return path + else: return null func _process(_delta): match state: diff --git a/client/settings.gd b/client/settings.gd new file mode 100644 index 00000000..2c3ae9a5 --- /dev/null +++ b/client/settings.gd @@ -0,0 +1,165 @@ +# Hurry Curry! - 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 <https://www.gnu.org/licenses/>. +# +extends Node +class_name Settings + +static func get_root(): + return SettingsRoot.new([ + SettingsCategory.new("gameplay", [ + ToggleSetting.new("interpolate_camera_rotation", false), + ToggleSetting.new("invert_camera", false), + ToggleSetting.new("usernames", true), + ToggleSetting.new("setup_complete", false), + ToggleSetting.new("tutorial_started", false), + ToggleSetting.new("latch_boost", true), + ]), + 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": 0, "lq_trees": false}, + "high": {"ui_blur": true, "aa": "ms2x", "ssao": true, "taa": false, "shadows": true, "glow": true, "grass_amount": 16, "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", 16 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()), + 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.input_map_to_settings(InputManager.default_input_map) + ), + SettingsCategory.new("other", [ + TextSetting.new("server_binary", ""), + TextSetting.new("server_data", ""), + ]) + ]) + +static func trigger_hook(key: String, value): + if Settings.change_hooks_display.get(key) != null: Settings.change_hooks_display.get(key).call(value) + if Settings.change_hooks_apply.get(key) != null: Settings.change_hooks_apply.get(key).call(value) + 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(Global.get_setting(key)) + +static func get_category_dict(prefix: String): + var map = {} + for k in Global.settings.keys(): + var kn = k.trim_prefix(prefix+".") + if kn == k: continue + map[kn] = Global.get_setting(k) + return map + +static var change_hooks_display = {} +static var change_hooks_apply = { + "input": h_input, + "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 apply_initial(): + for key in change_hooks_apply.keys(): + change_hooks_apply[key].call(Global.get_setting(key)) + +static func h_aa(mode): + match mode: + "disabled": + Global.get_viewport().msaa_2d = Viewport.MSAA_DISABLED + Global.get_viewport().msaa_3d = Viewport.MSAA_DISABLED + Global.get_viewport().screen_space_aa = Viewport.SCREEN_SPACE_AA_DISABLED + "fx": + Global.get_viewport().msaa_2d = Viewport.MSAA_DISABLED + Global.get_viewport().msaa_3d = Viewport.MSAA_DISABLED + Global.get_viewport().screen_space_aa = Viewport.SCREEN_SPACE_AA_FXAA + "ms2x": + Global.get_viewport().msaa_2d = Viewport.MSAA_2X + Global.get_viewport().msaa_3d = Viewport.MSAA_2X + Global.get_viewport().screen_space_aa = Viewport.SCREEN_SPACE_AA_DISABLED + "ms4x": + Global.get_viewport().msaa_2d = Viewport.MSAA_4X + Global.get_viewport().msaa_3d = Viewport.MSAA_4X + Global.get_viewport().screen_space_aa = Viewport.SCREEN_SPACE_AA_DISABLED + +static func h_taa(enabled): + Global.get_viewport().use_taa = enabled + +static func h_scale_mode(mode: String): + match mode: + "resize": Global.get_tree().root.content_scale_mode = Window.CONTENT_SCALE_MODE_CANVAS_ITEMS + "disabled": Global.get_tree().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_touch(mode: String): + match mode: + "enabled": Global.using_touch = true + "disabled": Global.using_touch = false + Global.using_touch_change.emit() + +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(_x): + InputManager.apply_input_map(Settings.get_category_dict("input")) + +# TODO whatever this does +# if not get_setting("tutorial_started"): +# for k in profile["hints"].keys(): +# set_hint(k, false) |