class_name SettingsPanel extends CanvasLayer ## Settings: mouse look sensitivity and HUD font scale, persisted through the ## `Save` autoload into `user://settings.json`. ## ## Emits `applied(settings)` so the game manager can push values into the guard ## and HUD as soon as the player confirms. signal applied(settings: Dictionary) signal closed() var _root: Control = null var _sensitivity: HSlider = null var _font_scale: HSlider = null var _sensitivity_value: Label = null var _font_scale_value: Label = null var settings: Dictionary = {} func _ready() -> void: layer = 22 _build() hide() func _init() -> void: layer = 22 _build() hide() func _build() -> void: _root = Control.new() _root.name = "SettingsRoot" _root.set_anchors_preset(Control.PRESET_FULL_RECT) _root.mouse_filter = Control.MOUSE_FILTER_STOP add_child(_root) var bg := ColorRect.new() bg.color = Color(0.02, 0.03, 0.05, 0.9) bg.set_anchors_preset(Control.PRESET_FULL_RECT) bg.mouse_filter = Control.MOUSE_FILTER_IGNORE _root.add_child(bg) var center := CenterContainer.new() center.set_anchors_preset(Control.PRESET_FULL_RECT) _root.add_child(center) var column := VBoxContainer.new() column.custom_minimum_size = Vector2(420, 0) column.add_theme_constant_override("separation", 12) center.add_child(column) var title := Label.new() title.text = "Settings" title.add_theme_font_size_override("font_size", 32) title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER column.add_child(title) var sens_row := VBoxContainer.new() column.add_child(sens_row) _sensitivity_value = _add_label_row(sens_row, "Mouse look sensitivity") _sensitivity = _add_slider(sens_row, 0.0005, 0.0060, 0.0001) _sensitivity.value_changed.connect(_on_sensitivity_changed) var font_row := VBoxContainer.new() column.add_child(font_row) _font_scale_value = _add_label_row(font_row, "HUD font scale") _font_scale = _add_slider(font_row, 0.75, 1.75, 0.05) _font_scale.value_changed.connect(_on_font_scale_changed) var row := HBoxContainer.new() row.alignment = BoxContainer.ALIGNMENT_CENTER row.add_theme_constant_override("separation", 12) column.add_child(row) var save_button := Button.new() save_button.name = "ApplyButton" save_button.text = "Apply" save_button.pressed.connect(_on_apply) row.add_child(save_button) var cancel_button := Button.new() cancel_button.name = "CancelButton" cancel_button.text = "Cancel" cancel_button.pressed.connect(_on_cancel) row.add_child(cancel_button) func _add_label_row(parent: Control, text: String) -> Label: var row := HBoxContainer.new() parent.add_child(row) var label := Label.new() label.text = text label.size_flags_horizontal = Control.SIZE_EXPAND_FILL row.add_child(label) var value := Label.new() value.add_theme_color_override("font_color", Color(0.75, 0.8, 0.9)) row.add_child(value) return value func _add_slider(parent: Control, min_v: float, max_v: float, step: float) -> HSlider: var slider := HSlider.new() slider.min_value = min_v slider.max_value = max_v slider.step = step slider.custom_minimum_size = Vector2(420, 24) parent.add_child(slider) return slider func open(current: Dictionary) -> void: settings = current.duplicate(true) var sens: float = float(settings.get("look_sensitivity", 0.0022)) var scale: float = float(settings.get("hud_font_scale", 1.0)) if _sensitivity: _sensitivity.set_value_no_signal(clampf(sens, _sensitivity.min_value, _sensitivity.max_value)) if _font_scale: _font_scale.set_value_no_signal(clampf(scale, _font_scale.min_value, _font_scale.max_value)) _sync_labels() visible = true _root.visible = true Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) func close() -> void: visible = false _root.visible = false func _sync_labels() -> void: if _sensitivity_value and _sensitivity: _sensitivity_value.text = "%.4f" % _sensitivity.value if _font_scale_value and _font_scale: _font_scale_value.text = "%.2fx" % _font_scale.value func _on_sensitivity_changed(value: float) -> void: settings["look_sensitivity"] = value _sync_labels() func _on_font_scale_changed(value: float) -> void: settings["hud_font_scale"] = value _sync_labels() func _on_apply() -> void: if Save: Save.save_settings(settings) applied.emit(settings) close() closed.emit() func _on_cancel() -> void: close() closed.emit() func _unhandled_input(event: InputEvent) -> void: if not visible: return if event is InputEventKey and event.pressed and not event.echo and event.keycode == KEY_ESCAPE: _on_cancel() get_viewport().set_input_as_handled()