From 23675d23db4384b319f17cf8128e52bc97f3b374 Mon Sep 17 00:00:00 2001 From: Billytifft Date: Sat, 3 Oct 2026 21:32:33 -0600 Subject: [PATCH 01/13] Add reusable Preferences scene and wire in menu + game --- C7/Audio/AudioManager.cs | 69 +++++++-- C7/C7Game.tscn | 14 +- C7/Game.cs | 19 ++- C7/Preferences.cs | 0 C7/PreferencesSettings.cs | 30 ++++ C7/UIElements/Civ3Checkbox.cs | 6 + C7/UIElements/MainMenu/MainMenu.cs | 10 +- C7/UIElements/MainMenu/main_menu.tscn | 4 + C7/UIElements/Popups/GameMenu.cs | 4 +- C7/UIElements/Popups/PopupOverlay.cs | 1 + C7/UIElements/Preferences.cs | 78 ++++++++++ C7/UIElements/Preferences.cs.uid | 0 C7/UIElements/preferences.tscn | 213 ++++++++++++++++++++++++++ C7/project.godot | 6 + C7Engine/Actions.cs | 1 + C7Engine/C7Settings.cs | 8 + 16 files changed, 441 insertions(+), 22 deletions(-) create mode 100644 C7/Preferences.cs create mode 100644 C7/PreferencesSettings.cs create mode 100644 C7/UIElements/Preferences.cs create mode 100644 C7/UIElements/Preferences.cs.uid create mode 100644 C7/UIElements/preferences.tscn diff --git a/C7/Audio/AudioManager.cs b/C7/Audio/AudioManager.cs index 34f9caec5..34d679848 100644 --- a/C7/Audio/AudioManager.cs +++ b/C7/Audio/AudioManager.cs @@ -1,5 +1,6 @@ using System; +using System.Collections.Generic; using C7Engine; using Godot; using Serilog; @@ -29,6 +30,16 @@ public partial class AudioManager : Node { private bool sfxAudioEnabled = true; private bool uiAudioEnabled = true; private bool ambienceAudioEnabled = true; + // Maps a bus to the settings key holding its volume. Public so settings UI + // can drive a bus without repeating the magic strings. + public static readonly Dictionary BusVolumeKeys = new() { + { MusicBus, "musicVolume" }, + { SfxAudioBus, "sfxAudioVolume" }, + { UIAudioBus, "uiAudioVolume" }, + { AmbienceAudioBus, "ambienceAudioVolume" }, + }; + + public override void _Ready() { log = LogManager.ForContext(); @@ -49,7 +60,7 @@ public override void _Ready() { private bool ConfigureVolume(string volumeKey, string audioBus) { try { string volume = C7Settings.GetSettingValue(AudioSettingsSection, volumeKey); - float volumeDb = LogicalVolumeAsDecibel(volume, volumeKey); + float volumeDb = LogicalVolumeAsDecibel(int.Parse(volume)); if (volumeDb == float.MinValue) { return false; @@ -75,22 +86,14 @@ private bool ConfigureVolume(string volumeKey, string audioBus) { * Our users are probably more used to a 0% to 100% system. * So this method converts between them. */ - private float LogicalVolumeAsDecibel(string volume, string volumeKey) { - if (volume == null) { - //First run. Save the setting. - C7Settings.SetValue(AudioSettingsSection, volumeKey, "100"); - C7Settings.SaveSettings(); - return 0; + public static float LogicalVolumeAsDecibel(int volume) { + if (volume <= 0) { + return float.MinValue; } - int userVolumeSetting = int.Parse(volume); - if (userVolumeSetting == 100) { + if (volume >= 100) { return 0; - } else if (userVolumeSetting == 0) { - return float.MinValue; - } else { - //Conversion math based on https://stackoverflow.com/a/37810295/3534605 - return 20.0f * (float)(Math.Log10(userVolumeSetting / 100.0f)); } + return 20.0f * (float)(Math.Log10(volume / 100.0f)); } // TODO: playlists, mixing, transitions @@ -123,6 +126,44 @@ public void StopMusic() { } } + + /// + /// The user's volume for a bus, as a 0-100 percentage, or -1 if the bus is + /// unknown. + /// + public int GetBusVolume(string bus) { + if (!BusVolumeKeys.TryGetValue(bus, out string key)) { + log.Warning("No volume setting known for bus {bus}", bus); + return -1; + } + string stored = C7Settings.GetSettingsValueOrDefault(AudioSettingsSection, key, "100"); + return int.TryParse(stored, out int volume) ? volume : 100; + } + + public void SetBusVolume(string bus, int volume) { + if (!BusVolumeKeys.TryGetValue(bus, out string key)) { + log.Warning("No volume setting known for bus {bus}", bus); + return; + } + volume = Mathf.Clamp(volume, 0, 100); + C7Settings.SetValue(AudioSettingsSection, key, volume.ToString()); + C7Settings.SaveSettings(); + SetBusVolumeDb(bus, LogicalVolumeAsDecibel(volume)); + } + + public void SetBusVolumePercent(string bus, int volume) { + SetBusVolumeDb(bus, LogicalVolumeAsDecibel(Mathf.Clamp(volume, 0, 100))); + } + + private void SetBusVolumeDb(string bus, float volumeDb) { + int busIndex = AudioServer.GetBusIndex(bus); + if (busIndex < 0) { + log.Warning("Audio bus {bus} not found", bus); + return; + } + AudioServer.SetBusVolumeDb(busIndex, volumeDb); + AudioServer.SetBusMute(busIndex, volumeDb == float.MinValue); + } public void PlaySfxAudio(string configKey) { var stream = AudioLoader.Load(configKey); if (stream != null) diff --git a/C7/C7Game.tscn b/C7/C7Game.tscn index 6db4d58cc..26a08631d 100644 --- a/C7/C7Game.tscn +++ b/C7/C7Game.tscn @@ -4,6 +4,7 @@ [ext_resource type="Script" uid="uid://cfenrvu7tnp40" path="res://DoubleClickHandler.cs" id="2_t735q"] [ext_resource type="Script" uid="uid://dflpmvue8o7uo" path="res://UnitSelector.cs" id="2_w8hq2"] [ext_resource type="Script" uid="uid://ifoasmoofo6r" path="res://UIElements/Advisors/Advisors.cs" id="3"] +[ext_resource type="PackedScene" path="res://UIElements/preferences.tscn" id="3_prefs"] [ext_resource type="Script" uid="uid://bvmc8fimjfo2j" path="res://Animations/AnimationController.cs" id="3_rmrhh"] [ext_resource type="Script" uid="uid://3wtlmr00bmfb" path="res://UIElements/UpperLeftNav/MenuButton.cs" id="4"] [ext_resource type="Script" uid="uid://cproyms6andlc" path="res://UIElements/UpperLeftNav/CivilopediaButton.cs" id="5"] @@ -30,7 +31,7 @@ [ext_resource type="PackedScene" uid="uid://bwe87nwkg01n1" path="res://UIElements/GameViews/space_race_view.tscn" id="25_y31gw"] [ext_resource type="PackedScene" uid="uid://dvrx5adwjd0w4" path="res://UIElements/GameViews/demographics_view.tscn" id="27_gbxbf"] -[node name="C7Game" type="Node" node_paths=PackedStringArray("Toolbar", "popupOverlay", "cityScreen", "advisor", "gameViews", "diplomacy", "doubleClickHandler", "animationController", "unitSelector")] +[node name="C7Game" type="Node" node_paths=PackedStringArray("Toolbar", "popupOverlay", "cityScreen", "advisor", "gameViews", "diplomacy", "preferences", "doubleClickHandler", "animationController", "unitSelector")] script = ExtResource("1") Toolbar = NodePath("CanvasLayer/Control/ToolBar/MarginContainer/HBoxContainer") popupOverlay = NodePath("CanvasLayer/PopupOverlay") @@ -38,6 +39,7 @@ cityScreen = NodePath("CanvasLayer/CityScreen") advisor = NodePath("CanvasLayer/Advisor") gameViews = NodePath("CanvasLayer/GameViews") diplomacy = NodePath("CanvasLayer/Diplomacy") +preferences = NodePath("CanvasLayer/Preferences") doubleClickHandler = NodePath("DoubleClickHandler") animationController = NodePath("AnimationController") unitSelector = NodePath("UnitSelector") @@ -198,6 +200,15 @@ script = ExtResource("8_7a2mf") text = "S" tooltipText = "Spaceship" +[node name="Preferences" parent="CanvasLayer" instance=ExtResource("3_prefs")] +visible = false +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + [node name="Advisor" type="CenterContainer" parent="CanvasLayer" node_paths=PackedStringArray("domesticAdvisor", "tradeAdvisor", "militaryAdvisor", "foreignAdvisor", "culturalAdvisor", "scienceAdvisor")] anchors_preset = 15 anchor_right = 1.0 @@ -335,3 +346,4 @@ visible = false [connection signal="Retire" from="CanvasLayer/PopupOverlay" to="." method="OnRetire"] [connection signal="SaveGame" from="CanvasLayer/PopupOverlay" to="." method="OnSaveGame"] [connection signal="LoadGame" from="CanvasLayer/PopupOverlay" to="." method="OnLoadGame"] +[connection signal="OpenPreferences" from="CanvasLayer/PopupOverlay" to="." method="OnOpenPreferences"] \ No newline at end of file diff --git a/C7/Game.cs b/C7/Game.cs index aa41d7889..b92723cbd 100644 --- a/C7/Game.cs +++ b/C7/Game.cs @@ -114,6 +114,8 @@ public partial class Game : Node { private GameViews gameViews; [Export] private Diplomacy diplomacy; + [Export] + private Preferences preferences; [Export] private DoubleClickHandler doubleClickHandler; @@ -581,6 +583,11 @@ public void OnLoadGame() { FileDialog.Popup(); } + public void OnOpenPreferences() { + popupOverlay.OnHidePopup(); + preferences.ShowPreferences(); + } + public void OnResolved() { popupOverlay.OnHidePopup(); } @@ -955,7 +962,7 @@ private bool HasVisibleModal() { if (popupOverlay.Visible || cityScreen.Visible || diplomacy.Visible) return true; - if (advisor.Visible || gameViews.Visible) + if (advisor.Visible || gameViews.Visible || preferences.Visible) return true; return false; @@ -990,6 +997,16 @@ private void ProcessAction(string currentAction) { return; } + if (currentAction == C7Action.OpenPreferences) { + preferences.ShowPreferences(); + return; + } + + if (currentAction == C7Action.Escape && preferences.Visible) { + preferences.HidePreferences(); + return; + } + if (currentAction == C7Action.Escape && gameViews.Visible) { gameViews.Hide(); return; diff --git a/C7/Preferences.cs b/C7/Preferences.cs new file mode 100644 index 000000000..e69de29bb diff --git a/C7/PreferencesSettings.cs b/C7/PreferencesSettings.cs new file mode 100644 index 000000000..b21022203 --- /dev/null +++ b/C7/PreferencesSettings.cs @@ -0,0 +1,30 @@ +using C7Engine; +using Godot; + +namespace C7.UIElements; + +/// +/// User preferences, stored outside the save file so they stay stable across +/// games and scenarios. Not yet surfaced anywhere in the UI. +/// +public static class PreferencesSettings { + public static class Game { + // Lower case on purpose: ini-parser treats section and key names as + // case-sensitive, and existing C7.ini files already use these names. + public const string SectionName = "preferences"; + public const string PromptForResearch = "promptForResearch"; + public const bool DefaultPromptForResearch = true; + } + + public static bool GetPromptForResearch() { + return C7Settings.GetBoolOrDefault( + Game.SectionName, + Game.PromptForResearch, + Game.DefaultPromptForResearch); + } + + public static void SetPromptForResearch(bool value) { + C7Settings.SetBool(Game.SectionName, Game.PromptForResearch, value); + C7Settings.SaveSettings(); + } +} diff --git a/C7/UIElements/Civ3Checkbox.cs b/C7/UIElements/Civ3Checkbox.cs index 97846cdae..d8b5a7dd7 100644 --- a/C7/UIElements/Civ3Checkbox.cs +++ b/C7/UIElements/Civ3Checkbox.cs @@ -16,6 +16,12 @@ public enum TextPosition { private Texture2D hoverTexture = TextureLoader.Load("ui.checkbox.hover"); private Texture2D pressedTexture = TextureLoader.Load("ui.checkbox.pressed"); + public Civ3Checkbox() { + normalTexture = TextureLoader.Load("ui.checkbox.inactive"); + hoverTexture = TextureLoader.Load("ui.checkbox.hover"); + pressedTexture = TextureLoader.Load("ui.checkbox.pressed"); + } + private string _text; [Export] public string Text { diff --git a/C7/UIElements/MainMenu/MainMenu.cs b/C7/UIElements/MainMenu/MainMenu.cs index df88633f3..609d8984d 100644 --- a/C7/UIElements/MainMenu/MainMenu.cs +++ b/C7/UIElements/MainMenu/MainMenu.cs @@ -16,6 +16,8 @@ public partial class MainMenu : Node { Civ3FileDialog LoadScenarioDialog; [Export] MenuButtonContainer ButtonContainer; + [Export] + Preferences preferences; GlobalSingleton Global; AudioManager AudioManager; @@ -63,9 +65,8 @@ private void DisplayTitleScreen() { ButtonContainer.LoadScenario.Pressed += LoadScenario; ButtonContainer.HallOfFame.Pressed += HallOfFame; ButtonContainer.HallOfFame.Visible = false; - ButtonContainer.Preferences.Pressed += Preferences; - ButtonContainer.Preferences.Visible = false; - ButtonContainer.AudioPreferences.Pressed += Preferences; + ButtonContainer.Preferences.Pressed += ShowPreferences; + ButtonContainer.AudioPreferences.Pressed += ShowPreferences; ButtonContainer.AudioPreferences.Visible = false; ButtonContainer.Credits.Pressed += showCredits; ButtonContainer.Exit.Pressed += _on_Exit_pressed; @@ -142,8 +143,9 @@ public void HallOfFame() { PlayButtonPressedSound(); } - public void Preferences() { + public void ShowPreferences() { PlayButtonPressedSound(); + preferences.ShowPreferences(); } public void _on_Exit_pressed() { diff --git a/C7/UIElements/MainMenu/main_menu.tscn b/C7/UIElements/MainMenu/main_menu.tscn index 11d419416..14a5b970f 100644 --- a/C7/UIElements/MainMenu/main_menu.tscn +++ b/C7/UIElements/MainMenu/main_menu.tscn @@ -6,6 +6,7 @@ [ext_resource type="FontFile" uid="uid://b1xkj8g82jbx5" path="res://Fonts/NotoSans-BoldItalic.ttf" id="3_x68dx"] [ext_resource type="Texture2D" uid="uid://ovwv08a53d7k" path="res://title.png" id="4_brea7"] [ext_resource type="PackedScene" uid="uid://bvncm1bgcii5e" path="res://UIElements/civ3_file_dialog.tscn" id="5_rw2g7"] +[ext_resource type="PackedScene" path="res://UIElements/preferences.tscn" id="6_prefs"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_vbij8"] @@ -228,6 +229,9 @@ visible = false [node name="LoadScenarioDialog" parent="." instance=ExtResource("5_rw2g7")] visible = false +[node name="Preferences" parent="." instance=ExtResource("6_prefs")] +visible = false + [connection signal="pressed" from="Control/Background/PanelContainer/MarginContainer/NoCiv3Options/SetCiv3Home" to="." method="_on_SetCiv3Home_pressed"] [connection signal="pressed" from="Control/Background/PanelContainer/MarginContainer/NoCiv3Options/UseStandaloneMode" to="." method="UseStandaloneModePressed"] [connection signal="dir_selected" from="SetCiv3HomeDialog" to="." method="_on_SetCiv3HomeDialog_dir_selected"] diff --git a/C7/UIElements/Popups/GameMenu.cs b/C7/UIElements/Popups/GameMenu.cs index 5d51ce845..e7929d870 100644 --- a/C7/UIElements/Popups/GameMenu.cs +++ b/C7/UIElements/Popups/GameMenu.cs @@ -24,7 +24,7 @@ public override void _Ready() { new("Load Game", Load), // new("New Game (Ctrl-Shift-Q)", NewGame), // TODO: Quick Start? - // new("Preferences (Ctrl-P)", OpenPreferences), + new("Preferences (Ctrl-P)", OpenPreferences), new("Retire", Retire), new("Save Game", Save), new("Quit Game (ESC)", Quit) @@ -67,6 +67,6 @@ private void NewGame() { } private void OpenPreferences() { - // TODO: Preferences management - disable animation, etc. + GetParent().EmitSignal(PopupOverlay.SignalName.OpenPreferences); } } diff --git a/C7/UIElements/Popups/PopupOverlay.cs b/C7/UIElements/Popups/PopupOverlay.cs index e01356b6c..b30564837 100644 --- a/C7/UIElements/Popups/PopupOverlay.cs +++ b/C7/UIElements/Popups/PopupOverlay.cs @@ -21,6 +21,7 @@ public partial class PopupOverlay : HBoxContainer { [Signal] public delegate void LoadGameEventHandler(); [Signal] public delegate void RetireEventHandler(); [Signal] public delegate void QuitEventHandler(); + [Signal] public delegate void OpenPreferencesEventHandler(); Control currentChild = null; diff --git a/C7/UIElements/Preferences.cs b/C7/UIElements/Preferences.cs new file mode 100644 index 000000000..9ee660d29 --- /dev/null +++ b/C7/UIElements/Preferences.cs @@ -0,0 +1,78 @@ +using C7.UIElements; +using Godot; + +/** + * User preferences: settings that stay stable across games and scenarios, so + * they are stored in C7.ini rather than in the save file. + * + * Deliberately not a PopupOverlay child. Preferences is a main menu style view, + * not a lightweight in-game modal, so it is a full scene shown and hidden in + * place, like the advisors. + */ +[GlobalClass] +public partial class Preferences : Control { + [Export] Civ3Checkbox promptForResearch; + [Export] Civ3HSlider musicVolume; + [Export] Civ3HSlider sfxVolume; + [Export] Civ3HSlider uiVolume; + [Export] Civ3HSlider ambienceVolume; + [Export] Label musicVolumeLabel; + [Export] Label sfxVolumeLabel; + [Export] Label uiVolumeLabel; + [Export] Label ambienceVolumeLabel; + [Export] Control musicVolumeRow; + [Export] Control sfxVolumeRow; + [Export] Control uiVolumeRow; + [Export] Control ambienceVolumeRow; + [Export] Civ3TextureButton close; + + private AudioManager audioManager; + + public override void _Ready() { + audioManager = GetNode("/root/GlobalAudioManager"); + + promptForResearch.ButtonPressed = PreferencesSettings.GetPromptForResearch(); + promptForResearch.Toggled += enabled => { + PreferencesSettings.SetPromptForResearch(enabled); + }; + + ConnectVolumeSlider(musicVolumeRow, musicVolume, musicVolumeLabel, AudioManager.MusicBus); + ConnectVolumeSlider(sfxVolumeRow, sfxVolume, sfxVolumeLabel, AudioManager.SfxAudioBus); + ConnectVolumeSlider(uiVolumeRow, uiVolume, uiVolumeLabel, AudioManager.UIAudioBus); + ConnectVolumeSlider(ambienceVolumeRow, ambienceVolume, ambienceVolumeLabel, AudioManager.AmbienceAudioBus); + + TextureLoader.SetButtonTextures(close, "ui.exit"); + close.Pressed += HidePreferences; + } + + private void ConnectVolumeSlider(Control row, Civ3HSlider slider, Label valueLabel, string bus) { + int volume = audioManager.GetBusVolume(bus); + if (volume < 0) { + row.Visible = false; + return; + } + + slider.Value = volume; + UpdateVolumeLabel(valueLabel, volume); + + slider.ValueChanged += value => { + UpdateVolumeLabel(valueLabel, (int)value); + audioManager.SetBusVolumePercent(bus, (int)value); + }; + slider.DragEnded += changed => { + audioManager.SetBusVolume(bus, (int)slider.Value); + }; + } + + private static void UpdateVolumeLabel(Label label, int volume) { + label.Text = volume == 0 ? "Off" : $"{volume}%"; + } + + public void ShowPreferences() { + Show(); + } + + public void HidePreferences() { + Hide(); + } +} diff --git a/C7/UIElements/Preferences.cs.uid b/C7/UIElements/Preferences.cs.uid new file mode 100644 index 000000000..e69de29bb diff --git a/C7/UIElements/preferences.tscn b/C7/UIElements/preferences.tscn new file mode 100644 index 000000000..11bd8bfb9 --- /dev/null +++ b/C7/UIElements/preferences.tscn @@ -0,0 +1,213 @@ +[gd_scene load_steps=5 format=3] + +[ext_resource type="Script" path="res://UIElements/Preferences.cs" id="1_prefs"] +[ext_resource type="Script" uid="uid://b4grm7mhaqgri" path="res://UIElements/Civ3Checkbox.cs" id="2_checkbox"] +[ext_resource type="Script" uid="uid://clv4mgavtlrul" path="res://UIElements/Civ3HSlider.cs" id="3_slider"] +[ext_resource type="Script" uid="uid://dcrdcjop64t6t" path="res://UIElements/Civ3TextureButton.cs" id="4_close"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_panel"] +bg_color = Color(0.117647, 0.101961, 0.0784314, 0.941176) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.72549, 0.643137, 0.439216, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[node name="Preferences" type="Control" node_paths=PackedStringArray("promptForResearch", "musicVolume", "sfxVolume", "uiVolume", "ambienceVolume", "musicVolumeLabel", "sfxVolumeLabel", "uiVolumeLabel", "ambienceVolumeLabel", "musicVolumeRow", "sfxVolumeRow", "uiVolumeRow", "ambienceVolumeRow", "close")] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_prefs") +promptForResearch = NodePath("Margin/Panel/Rows/PromptForResearch") +musicVolume = NodePath("Margin/Panel/Rows/MusicVolumeRow/MusicVolume") +sfxVolume = NodePath("Margin/Panel/Rows/SfxVolumeRow/SfxVolume") +uiVolume = NodePath("Margin/Panel/Rows/UiVolumeRow/UiVolume") +ambienceVolume = NodePath("Margin/Panel/Rows/AmbienceVolumeRow/AmbienceVolume") +musicVolumeLabel = NodePath("Margin/Panel/Rows/MusicVolumeRow/MusicVolumeValue") +sfxVolumeLabel = NodePath("Margin/Panel/Rows/SfxVolumeRow/SfxVolumeValue") +uiVolumeLabel = NodePath("Margin/Panel/Rows/UiVolumeRow/UiVolumeValue") +ambienceVolumeLabel = NodePath("Margin/Panel/Rows/AmbienceVolumeRow/AmbienceVolumeValue") +musicVolumeRow = NodePath("Margin/Panel/Rows/MusicVolumeRow") +sfxVolumeRow = NodePath("Margin/Panel/Rows/SfxVolumeRow") +uiVolumeRow = NodePath("Margin/Panel/Rows/UiVolumeRow") +ambienceVolumeRow = NodePath("Margin/Panel/Rows/AmbienceVolumeRow") +close = NodePath("Margin/Panel/Rows/Footer/Close") + +[node name="Dim" type="ColorRect" parent="."] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0, 0, 0, 0.588235) + +[node name="Margin" type="MarginContainer" parent="."] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 60 +theme_override_constants/margin_top = 48 +theme_override_constants/margin_right = 60 +theme_override_constants/margin_bottom = 48 + +[node name="Panel" type="PanelContainer" parent="Margin"] +theme_override_styles/panel = SubResource("StyleBoxFlat_panel") + +[node name="Rows" type="VBoxContainer" parent="Margin/Panel"] +theme_override_constants/separation = 10 + +[node name="Title" type="Label" parent="Margin/Panel/Rows"] +layout_mode = 2 +theme_override_font_sizes/font_size = 26 +text = "PREFERENCES" + +[node name="Separator" type="HSeparator" parent="Margin/Panel/Rows"] +layout_mode = 2 + +[node name="GameHeading" type="Label" parent="Margin/Panel/Rows"] +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +text = "Game" + +[node name="PromptForResearch" type="CheckBox" parent="Margin/Panel/Rows"] +layout_mode = 2 +toggle_mode = true +script = ExtResource("2_checkbox") +Text = "Ask which tech to research" +FontSize = 13 + +[node name="UnitsHeading" type="Label" parent="Margin/Panel/Rows"] +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +text = "Units" + +[node name="UnitsNote" type="Label" parent="Margin/Panel/Rows"] +layout_mode = 2 +theme_override_font_sizes/font_size = 12 +text = "No unit options available yet." +autowrap_mode = 2 + +[node name="AudioHeading" type="Label" parent="Margin/Panel/Rows"] +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +text = "Audio" + +[node name="MusicVolumeRow" type="HBoxContainer" parent="Margin/Panel/Rows"] +layout_mode = 2 +theme_override_constants/separation = 12 + +[node name="MusicVolumeLabel" type="Label" parent="Margin/Panel/Rows/MusicVolumeRow"] +custom_minimum_size = Vector2(110, 0) +layout_mode = 2 +text = "Music" + +[node name="MusicVolume" type="HSlider" parent="Margin/Panel/Rows/MusicVolumeRow"] +custom_minimum_size = Vector2(220, 0) +layout_mode = 2 +size_flags_horizontal = 3 +max_value = 100.0 +step = 1.0 +value = 100.0 +script = ExtResource("3_slider") +metadata/_custom_type_script = "uid://clv4mgavtlrul" + +[node name="MusicVolumeValue" type="Label" parent="Margin/Panel/Rows/MusicVolumeRow"] +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +text = "100%" +horizontal_alignment = 2 + +[node name="SfxVolumeRow" type="HBoxContainer" parent="Margin/Panel/Rows"] +layout_mode = 2 +theme_override_constants/separation = 12 + +[node name="SfxVolumeLabel" type="Label" parent="Margin/Panel/Rows/SfxVolumeRow"] +custom_minimum_size = Vector2(110, 0) +layout_mode = 2 +text = "Sound effects" + +[node name="SfxVolume" type="HSlider" parent="Margin/Panel/Rows/SfxVolumeRow"] +custom_minimum_size = Vector2(220, 0) +layout_mode = 2 +size_flags_horizontal = 3 +max_value = 100.0 +step = 1.0 +value = 100.0 +script = ExtResource("3_slider") +metadata/_custom_type_script = "uid://clv4mgavtlrul" + +[node name="SfxVolumeValue" type="Label" parent="Margin/Panel/Rows/SfxVolumeRow"] +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +text = "100%" +horizontal_alignment = 2 + +[node name="UiVolumeRow" type="HBoxContainer" parent="Margin/Panel/Rows"] +layout_mode = 2 +theme_override_constants/separation = 12 + +[node name="UiVolumeLabel" type="Label" parent="Margin/Panel/Rows/UiVolumeRow"] +custom_minimum_size = Vector2(110, 0) +layout_mode = 2 +text = "Interface" + +[node name="UiVolume" type="HSlider" parent="Margin/Panel/Rows/UiVolumeRow"] +custom_minimum_size = Vector2(220, 0) +layout_mode = 2 +size_flags_horizontal = 3 +max_value = 100.0 +step = 1.0 +value = 100.0 +script = ExtResource("3_slider") +metadata/_custom_type_script = "uid://clv4mgavtlrul" + +[node name="UiVolumeValue" type="Label" parent="Margin/Panel/Rows/UiVolumeRow"] +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +text = "100%" +horizontal_alignment = 2 + +[node name="AmbienceVolumeRow" type="HBoxContainer" parent="Margin/Panel/Rows"] +layout_mode = 2 +theme_override_constants/separation = 12 + +[node name="AmbienceVolumeLabel" type="Label" parent="Margin/Panel/Rows/AmbienceVolumeRow"] +custom_minimum_size = Vector2(110, 0) +layout_mode = 2 +text = "Ambience" + +[node name="AmbienceVolume" type="HSlider" parent="Margin/Panel/Rows/AmbienceVolumeRow"] +custom_minimum_size = Vector2(220, 0) +layout_mode = 2 +size_flags_horizontal = 3 +max_value = 100.0 +step = 1.0 +value = 100.0 +script = ExtResource("3_slider") +metadata/_custom_type_script = "uid://clv4mgavtlrul" + +[node name="AmbienceVolumeValue" type="Label" parent="Margin/Panel/Rows/AmbienceVolumeRow"] +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +text = "100%" +horizontal_alignment = 2 + +[node name="FooterSeparator" type="HSeparator" parent="Margin/Panel/Rows"] +layout_mode = 2 + +[node name="Footer" type="HBoxContainer" parent="Margin/Panel/Rows"] +layout_mode = 2 +alignment = 2 + +[node name="Close" type="TextureButton" parent="Margin/Panel/Rows/Footer"] +custom_minimum_size = Vector2(72, 19) +layout_mode = 2 +script = ExtResource("4_close") diff --git a/C7/project.godot b/C7/project.godot index 54a5be390..7f45b2afb 100644 --- a/C7/project.godot +++ b/C7/project.godot @@ -46,6 +46,12 @@ theme/custom="res://C7Theme.tres" [input] +open_preferences={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":80,"key_label":0,"unicode":112,"location":0,"echo":false,"script":null) +] +} + left={ "deadzone": 0.5, "events": [] diff --git a/C7Engine/Actions.cs b/C7Engine/Actions.cs index 38a1e8063..f4a657207 100644 --- a/C7Engine/Actions.cs +++ b/C7Engine/Actions.cs @@ -23,6 +23,7 @@ public static class C7Action { public const string ToggleGrid = "toggle_grid"; public const string ToggleCoordinates = "toggle_coordinates"; public const string ToggleZoom = "toggle_zoom"; + public const string OpenPreferences = "open_preferences"; // Ctrl-P public const string ShowDomesticAdvisor = "show_domestic_advisor"; // F1 public const string ShowTradeAdvisor = "show_trade_advisor"; diff --git a/C7Engine/C7Settings.cs b/C7Engine/C7Settings.cs index d787267c7..40384bd2e 100644 --- a/C7Engine/C7Settings.cs +++ b/C7Engine/C7Settings.cs @@ -51,6 +51,14 @@ public static string GetSettingValue(string section, string key) { return settings[section][key]; } + + public static bool GetBoolOrDefault(string section, string key, bool defaultValue) { + return GetSettingsValueOrDefault(section, key, defaultValue ? "true" : "false") == "true"; + } + + public static void SetBool(string section, string key, bool value) { + SetValue(section, key, value ? "true" : "false"); + } public static string GetSettingsValueOrDefault(string section, string key, string defaultValue) { if (settings == null) { LoadSettings(); From 110e656279d9d1d6bab9f059ca3b49efca7f5176 Mon Sep 17 00:00:00 2001 From: Billytifft Date: Sun, 4 Oct 2026 19:06:13 -0600 Subject: [PATCH 02/13] Fix main_menu preferences export --- C7/UIElements/MainMenu/main_menu.tscn | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/C7/UIElements/MainMenu/main_menu.tscn b/C7/UIElements/MainMenu/main_menu.tscn index 14a5b970f..90d7fdb04 100644 --- a/C7/UIElements/MainMenu/main_menu.tscn +++ b/C7/UIElements/MainMenu/main_menu.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=12 format=3 uid="uid://gnt0y3og7nk6"] +[gd_scene load_steps=13 format=3 uid="uid://gnt0y3og7nk6"] [ext_resource type="Script" uid="uid://dgpuse88jc2hc" path="res://UIElements/MainMenu/MainMenu.cs" id="1_akneh"] [ext_resource type="Texture2D" uid="uid://s55xvcaini8h" path="res://title-screen.png" id="2_erxyj"] @@ -22,14 +22,13 @@ font_size = 10 font_color = Color(0, 0, 0, 1) shadow_color = Color(1, 1, 1, 0.533333) -[node name="MainMenu" type="Node" node_paths=PackedStringArray("LoadDialog", "NoCiv3Options", "SetCiv3HomeDialog", "LoadScenarioDialog", "ButtonContainer", "player")] +[node name="MainMenu" type="Node" node_paths=PackedStringArray("LoadDialog", "NoCiv3Options", "SetCiv3HomeDialog", "LoadScenarioDialog", "ButtonContainer", "preferences")] script = ExtResource("1_akneh") LoadDialog = NodePath("LoadDialog") NoCiv3Options = NodePath("Control/Background/PanelContainer/MarginContainer/NoCiv3Options") SetCiv3HomeDialog = NodePath("SetCiv3HomeDialog") LoadScenarioDialog = NodePath("LoadScenarioDialog") ButtonContainer = NodePath("Control/Background/PanelContainer/MarginContainer/MainMenuContainer") -player = NodePath("SoundEffectPlayer") [node name="Control" type="Control" parent="."] layout_mode = 3 @@ -231,6 +230,7 @@ visible = false [node name="Preferences" parent="." instance=ExtResource("6_prefs")] visible = false +layout_mode = 3 [connection signal="pressed" from="Control/Background/PanelContainer/MarginContainer/NoCiv3Options/SetCiv3Home" to="." method="_on_SetCiv3Home_pressed"] [connection signal="pressed" from="Control/Background/PanelContainer/MarginContainer/NoCiv3Options/UseStandaloneMode" to="." method="UseStandaloneModePressed"] From 78ec4d21521ced927825ebe182b0424b479cabb5 Mon Sep 17 00:00:00 2001 From: Billytifft Date: Sun, 4 Oct 2026 19:14:21 -0600 Subject: [PATCH 03/13] Fix main_menu preferences export wiring --- C7/UIElements/MainMenu/main_menu.tscn | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/C7/UIElements/MainMenu/main_menu.tscn b/C7/UIElements/MainMenu/main_menu.tscn index 90d7fdb04..8cc2f386a 100644 --- a/C7/UIElements/MainMenu/main_menu.tscn +++ b/C7/UIElements/MainMenu/main_menu.tscn @@ -22,7 +22,7 @@ font_size = 10 font_color = Color(0, 0, 0, 1) shadow_color = Color(1, 1, 1, 0.533333) -[node name="MainMenu" type="Node" node_paths=PackedStringArray("LoadDialog", "NoCiv3Options", "SetCiv3HomeDialog", "LoadScenarioDialog", "ButtonContainer", "preferences")] +[node name="MainMenu" type="Node" node_paths=PackedStringArray("LoadDialog", "NoCiv3Options", "SetCiv3HomeDialog", "LoadScenarioDialog", "ButtonContainer")] script = ExtResource("1_akneh") LoadDialog = NodePath("LoadDialog") NoCiv3Options = NodePath("Control/Background/PanelContainer/MarginContainer/NoCiv3Options") @@ -147,6 +147,17 @@ horizontal_alignment = 1 layout_mode = 2 theme_override_constants/separation = 15 script = ExtResource("3_t1nuq") +NewGame = null +QuickStart = null +Tutorial = null +LoadGame = null +LoadScenario = null +HallOfFame = null +ToggleGraphics = null +Preferences = null +AudioPreferences = null +Credits = null +Exit = null [node name="MarginContainer" type="MarginContainer" parent="Control/Background"] layout_mode = 1 From d076e3b62452a535a19320393be543e60b3b5384 Mon Sep 17 00:00:00 2001 From: Billytifft Date: Sun, 4 Oct 2026 19:21:22 -0600 Subject: [PATCH 04/13] Fix main menu preferences export path --- C7/UIElements/MainMenu/main_menu.tscn | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/C7/UIElements/MainMenu/main_menu.tscn b/C7/UIElements/MainMenu/main_menu.tscn index 8cc2f386a..9a77091e2 100644 --- a/C7/UIElements/MainMenu/main_menu.tscn +++ b/C7/UIElements/MainMenu/main_menu.tscn @@ -22,13 +22,14 @@ font_size = 10 font_color = Color(0, 0, 0, 1) shadow_color = Color(1, 1, 1, 0.533333) -[node name="MainMenu" type="Node" node_paths=PackedStringArray("LoadDialog", "NoCiv3Options", "SetCiv3HomeDialog", "LoadScenarioDialog", "ButtonContainer")] +[node name="MainMenu" type="Node" node_paths=PackedStringArray("LoadDialog", "NoCiv3Options", "SetCiv3HomeDialog", "LoadScenarioDialog", "ButtonContainer", "preferences")] script = ExtResource("1_akneh") LoadDialog = NodePath("LoadDialog") NoCiv3Options = NodePath("Control/Background/PanelContainer/MarginContainer/NoCiv3Options") SetCiv3HomeDialog = NodePath("SetCiv3HomeDialog") LoadScenarioDialog = NodePath("LoadScenarioDialog") ButtonContainer = NodePath("Control/Background/PanelContainer/MarginContainer/MainMenuContainer") +preferences = NodePath("Preferences") [node name="Control" type="Control" parent="."] layout_mode = 3 @@ -245,4 +246,4 @@ layout_mode = 3 [connection signal="pressed" from="Control/Background/PanelContainer/MarginContainer/NoCiv3Options/SetCiv3Home" to="." method="_on_SetCiv3Home_pressed"] [connection signal="pressed" from="Control/Background/PanelContainer/MarginContainer/NoCiv3Options/UseStandaloneMode" to="." method="UseStandaloneModePressed"] -[connection signal="dir_selected" from="SetCiv3HomeDialog" to="." method="_on_SetCiv3HomeDialog_dir_selected"] +[connection signal="dir_selected" from="SetCiv3HomeDialog" to="." method="_on_SetCiv3HomeDialog_dir_selected"] \ No newline at end of file From ab953c032776ede00f5a1fbd5c38f56361109526 Mon Sep 17 00:00:00 2001 From: Billytifft Date: Sun, 4 Oct 2026 19:30:48 -0600 Subject: [PATCH 05/13] Clean up Civ3Checkbox texture initialization --- C7/UIElements/Civ3Checkbox.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/C7/UIElements/Civ3Checkbox.cs b/C7/UIElements/Civ3Checkbox.cs index d8b5a7dd7..581bd8332 100644 --- a/C7/UIElements/Civ3Checkbox.cs +++ b/C7/UIElements/Civ3Checkbox.cs @@ -13,8 +13,8 @@ public enum TextPosition { } private Texture2D normalTexture = TextureLoader.Load("ui.checkbox.inactive"); - private Texture2D hoverTexture = TextureLoader.Load("ui.checkbox.hover"); - private Texture2D pressedTexture = TextureLoader.Load("ui.checkbox.pressed"); + private Texture2D hoverTexture; + private Texture2D pressedTexture; public Civ3Checkbox() { normalTexture = TextureLoader.Load("ui.checkbox.inactive"); From 28f49597581fc53aadcf8511201f3c84fcb78e73 Mon Sep 17 00:00:00 2001 From: Billytifft Date: Sun, 4 Oct 2026 19:41:11 -0600 Subject: [PATCH 06/13] Tweak preferences panel background --- C7/UIElements/preferences.tscn | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/C7/UIElements/preferences.tscn b/C7/UIElements/preferences.tscn index 11bd8bfb9..ab4920c4c 100644 --- a/C7/UIElements/preferences.tscn +++ b/C7/UIElements/preferences.tscn @@ -6,7 +6,7 @@ [ext_resource type="Script" uid="uid://dcrdcjop64t6t" path="res://UIElements/Civ3TextureButton.cs" id="4_close"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_panel"] -bg_color = Color(0.117647, 0.101961, 0.0784314, 0.941176) +bg_color = Color(0.156863, 0.137255, 0.109804, 0.960784) border_width_left = 2 border_width_top = 2 border_width_right = 2 @@ -45,7 +45,7 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -color = Color(0, 0, 0, 0.588235) +color = Color(0, 0, 0, 0.4) [node name="Margin" type="MarginContainer" parent="."] anchors_preset = 15 From 503236ae1c19c83b661af0894679f6f5ab39ca38 Mon Sep 17 00:00:00 2001 From: Billytifft Date: Sun, 4 Oct 2026 19:52:40 -0600 Subject: [PATCH 07/13] Tweak preferences layout: move checkbox to left column in 3-column grid --- C7/UIElements/preferences.tscn | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/C7/UIElements/preferences.tscn b/C7/UIElements/preferences.tscn index ab4920c4c..d88988548 100644 --- a/C7/UIElements/preferences.tscn +++ b/C7/UIElements/preferences.tscn @@ -24,7 +24,7 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 script = ExtResource("1_prefs") -promptForResearch = NodePath("Margin/Panel/Rows/PromptForResearch") +promptForResearch = NodePath("Margin/Panel/Rows/GameGrid/PromptForResearch") musicVolume = NodePath("Margin/Panel/Rows/MusicVolumeRow/MusicVolume") sfxVolume = NodePath("Margin/Panel/Rows/SfxVolumeRow/SfxVolume") uiVolume = NodePath("Margin/Panel/Rows/UiVolumeRow/UiVolume") @@ -77,7 +77,12 @@ layout_mode = 2 theme_override_font_sizes/font_size = 18 text = "Game" -[node name="PromptForResearch" type="CheckBox" parent="Margin/Panel/Rows"] +[node name="GameGrid" type="GridContainer" parent="Margin/Panel/Rows"] +layout_mode = 2 +columns = 3 +column_min_width = 220 + +[node name="PromptForResearch" type="CheckBox" parent="Margin/Panel/Rows/GameGrid"] layout_mode = 2 toggle_mode = true script = ExtResource("2_checkbox") From 27e96de58171b3ae434f1d3499f8e997f987f0ee Mon Sep 17 00:00:00 2001 From: Billytifft Date: Sun, 4 Oct 2026 20:01:06 -0600 Subject: [PATCH 08/13] Default Civ3Checkbox text to right of icon --- C7/UIElements/Civ3Checkbox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/C7/UIElements/Civ3Checkbox.cs b/C7/UIElements/Civ3Checkbox.cs index 581bd8332..74ee3adb0 100644 --- a/C7/UIElements/Civ3Checkbox.cs +++ b/C7/UIElements/Civ3Checkbox.cs @@ -44,7 +44,7 @@ public int FontSize { } } } - private TextPosition _textPosition; + private TextPosition _textPosition = TextPosition.TextRightOfIcon; [Export] public TextPosition textPosition { get => _textPosition; From 5baf77e42d8818183d8467e6164cc205bc43d0d1 Mon Sep 17 00:00:00 2001 From: Billytifft Date: Sun, 4 Oct 2026 20:34:16 -0600 Subject: [PATCH 09/13] Use advisor wood panel background for preferences The preferences view used a hand-built dark StyleBoxFlat panel. Use the generic advisor frame instead (screens.wonders.background): wood panelling around a light tan interior, with no baked-in widgets. advisors.domestic was the wrong source because it carries the green/red commerce blocks. Layout follows the advisors: root CenterContainer so the 1024x768 texture keeps its native size, title drawn by AdvisorUtils, Game and Units as three-column grids, and the checkbox in the leftmost Game column with its icon left of the label. Also grow the checkbox to cover its own label. Godot cached the bare CheckBox minimum size before the icon/text container was built, so only the 15x15 icon was clickable. --- C7/Audio/AudioManager.cs | 2 +- C7/Game.cs | 2 +- C7/UIElements/Civ3Checkbox.cs | 14 ++ C7/UIElements/MainMenu/main_menu.tscn | 2 +- C7/UIElements/Preferences.cs | 6 + C7/UIElements/preferences.tscn | 280 +++++++++++++++----------- 6 files changed, 180 insertions(+), 126 deletions(-) diff --git a/C7/Audio/AudioManager.cs b/C7/Audio/AudioManager.cs index 34d679848..792b34e94 100644 --- a/C7/Audio/AudioManager.cs +++ b/C7/Audio/AudioManager.cs @@ -86,7 +86,7 @@ private bool ConfigureVolume(string volumeKey, string audioBus) { * Our users are probably more used to a 0% to 100% system. * So this method converts between them. */ - public static float LogicalVolumeAsDecibel(int volume) { + public static float LogicalVolumeAsDecibel(int volume) { if (volume <= 0) { return float.MinValue; } diff --git a/C7/Game.cs b/C7/Game.cs index b92723cbd..b78d7ba6a 100644 --- a/C7/Game.cs +++ b/C7/Game.cs @@ -583,7 +583,7 @@ public void OnLoadGame() { FileDialog.Popup(); } - public void OnOpenPreferences() { + public void OnOpenPreferences() { popupOverlay.OnHidePopup(); preferences.ShowPreferences(); } diff --git a/C7/UIElements/Civ3Checkbox.cs b/C7/UIElements/Civ3Checkbox.cs index 74ee3adb0..35904c0f4 100644 --- a/C7/UIElements/Civ3Checkbox.cs +++ b/C7/UIElements/Civ3Checkbox.cs @@ -30,6 +30,7 @@ public string Text { _text = value; if (label != null) { label.Text = _text; + RefreshMinimumSize(); } } } @@ -41,6 +42,7 @@ public int FontSize { _fontSize = value; if (label != null) { label.AddThemeFontSizeOverride("font_size", _fontSize); + RefreshMinimumSize(); } } } @@ -145,6 +147,18 @@ private void SetUpLayout() { boxContainer.AddChild(textureRect); boxContainer.AddChild(label); } + + RefreshMinimumSize(); + } + + // Grow the button to cover the icon and the label so the whole thing is + // clickable. Godot caches the bare CheckBox size, so it has to be set + // explicitly whenever the contents change. + private void RefreshMinimumSize() { + if (boxContainer == null) { + return; + } + CustomMinimumSize = boxContainer.GetCombinedMinimumSize(); } // Expand the size of the button to contain the texture and the label. diff --git a/C7/UIElements/MainMenu/main_menu.tscn b/C7/UIElements/MainMenu/main_menu.tscn index 9a77091e2..3d83f2de2 100644 --- a/C7/UIElements/MainMenu/main_menu.tscn +++ b/C7/UIElements/MainMenu/main_menu.tscn @@ -246,4 +246,4 @@ layout_mode = 3 [connection signal="pressed" from="Control/Background/PanelContainer/MarginContainer/NoCiv3Options/SetCiv3Home" to="." method="_on_SetCiv3Home_pressed"] [connection signal="pressed" from="Control/Background/PanelContainer/MarginContainer/NoCiv3Options/UseStandaloneMode" to="." method="UseStandaloneModePressed"] -[connection signal="dir_selected" from="SetCiv3HomeDialog" to="." method="_on_SetCiv3HomeDialog_dir_selected"] \ No newline at end of file +[connection signal="dir_selected" from="SetCiv3HomeDialog" to="." method="_on_SetCiv3HomeDialog_dir_selected"] diff --git a/C7/UIElements/Preferences.cs b/C7/UIElements/Preferences.cs index 9ee660d29..728bad4af 100644 --- a/C7/UIElements/Preferences.cs +++ b/C7/UIElements/Preferences.cs @@ -25,12 +25,18 @@ public partial class Preferences : Control { [Export] Control uiVolumeRow; [Export] Control ambienceVolumeRow; [Export] Civ3TextureButton close; + [Export] TextureRect background; private AudioManager audioManager; public override void _Ready() { audioManager = GetNode("/root/GlobalAudioManager"); + // Match the advisor screens: the background texture is set on the + // TextureRect via textureConfigKey, and the title is drawn with the + // shared helper so the font and spacing match the other full-screen views. + AdvisorUtils.CreateAdvisorTitle(background, background.Texture.GetWidth(), "PREFERENCES"); + promptForResearch.ButtonPressed = PreferencesSettings.GetPromptForResearch(); promptForResearch.Toggled += enabled => { PreferencesSettings.SetPromptForResearch(enabled); diff --git a/C7/UIElements/preferences.tscn b/C7/UIElements/preferences.tscn index d88988548..d174712ba 100644 --- a/C7/UIElements/preferences.tscn +++ b/C7/UIElements/preferences.tscn @@ -1,218 +1,252 @@ -[gd_scene load_steps=5 format=3] +[gd_scene load_steps=6 format=3] [ext_resource type="Script" path="res://UIElements/Preferences.cs" id="1_prefs"] -[ext_resource type="Script" uid="uid://b4grm7mhaqgri" path="res://UIElements/Civ3Checkbox.cs" id="2_checkbox"] -[ext_resource type="Script" uid="uid://clv4mgavtlrul" path="res://UIElements/Civ3HSlider.cs" id="3_slider"] -[ext_resource type="Script" uid="uid://dcrdcjop64t6t" path="res://UIElements/Civ3TextureButton.cs" id="4_close"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_panel"] -bg_color = Color(0.156863, 0.137255, 0.109804, 0.960784) -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.72549, 0.643137, 0.439216, 1) -corner_radius_top_left = 4 -corner_radius_top_right = 4 -corner_radius_bottom_right = 4 -corner_radius_bottom_left = 4 - -[node name="Preferences" type="Control" node_paths=PackedStringArray("promptForResearch", "musicVolume", "sfxVolume", "uiVolume", "ambienceVolume", "musicVolumeLabel", "sfxVolumeLabel", "uiVolumeLabel", "ambienceVolumeLabel", "musicVolumeRow", "sfxVolumeRow", "uiVolumeRow", "ambienceVolumeRow", "close")] -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -script = ExtResource("1_prefs") -promptForResearch = NodePath("Margin/Panel/Rows/GameGrid/PromptForResearch") -musicVolume = NodePath("Margin/Panel/Rows/MusicVolumeRow/MusicVolume") -sfxVolume = NodePath("Margin/Panel/Rows/SfxVolumeRow/SfxVolume") -uiVolume = NodePath("Margin/Panel/Rows/UiVolumeRow/UiVolume") -ambienceVolume = NodePath("Margin/Panel/Rows/AmbienceVolumeRow/AmbienceVolume") -musicVolumeLabel = NodePath("Margin/Panel/Rows/MusicVolumeRow/MusicVolumeValue") -sfxVolumeLabel = NodePath("Margin/Panel/Rows/SfxVolumeRow/SfxVolumeValue") -uiVolumeLabel = NodePath("Margin/Panel/Rows/UiVolumeRow/UiVolumeValue") -ambienceVolumeLabel = NodePath("Margin/Panel/Rows/AmbienceVolumeRow/AmbienceVolumeValue") -musicVolumeRow = NodePath("Margin/Panel/Rows/MusicVolumeRow") -sfxVolumeRow = NodePath("Margin/Panel/Rows/SfxVolumeRow") -uiVolumeRow = NodePath("Margin/Panel/Rows/UiVolumeRow") -ambienceVolumeRow = NodePath("Margin/Panel/Rows/AmbienceVolumeRow") -close = NodePath("Margin/Panel/Rows/Footer/Close") - -[node name="Dim" type="ColorRect" parent="."] -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -color = Color(0, 0, 0, 0.4) +[ext_resource type="Script" uid="uid://doqipc4rnaotx" path="res://UIElements/Civ3TextureRect.cs" id="2_bg"] +[ext_resource type="Script" uid="uid://b4grm7mhaqgri" path="res://UIElements/Civ3Checkbox.cs" id="3_checkbox"] +[ext_resource type="Script" uid="uid://clv4mgavtlrul" path="res://UIElements/Civ3HSlider.cs" id="4_slider"] +[ext_resource type="Script" uid="uid://dcrdcjop64t6t" path="res://UIElements/Civ3TextureButton.cs" id="5_close"] -[node name="Margin" type="MarginContainer" parent="."] +[node name="Preferences" type="CenterContainer" node_paths=PackedStringArray("background", "promptForResearch", "musicVolume", "sfxVolume", "uiVolume", "ambienceVolume", "musicVolumeLabel", "sfxVolumeLabel", "uiVolumeLabel", "ambienceVolumeLabel", "musicVolumeRow", "sfxVolumeRow", "uiVolumeRow", "ambienceVolumeRow", "close")] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -theme_override_constants/margin_left = 60 -theme_override_constants/margin_top = 48 -theme_override_constants/margin_right = 60 -theme_override_constants/margin_bottom = 48 - -[node name="Panel" type="PanelContainer" parent="Margin"] -theme_override_styles/panel = SubResource("StyleBoxFlat_panel") - -[node name="Rows" type="VBoxContainer" parent="Margin/Panel"] -theme_override_constants/separation = 10 - -[node name="Title" type="Label" parent="Margin/Panel/Rows"] -layout_mode = 2 -theme_override_font_sizes/font_size = 26 -text = "PREFERENCES" - -[node name="Separator" type="HSeparator" parent="Margin/Panel/Rows"] -layout_mode = 2 - -[node name="GameHeading" type="Label" parent="Margin/Panel/Rows"] -layout_mode = 2 +mouse_filter = 0 +script = ExtResource("1_prefs") +background = NodePath("Background") +promptForResearch = NodePath("Background/Rows/GameGrid/PromptForResearch") +musicVolume = NodePath("Background/Rows/MusicVolumeRow/MusicVolume") +sfxVolume = NodePath("Background/Rows/SfxVolumeRow/SfxVolume") +uiVolume = NodePath("Background/Rows/UiVolumeRow/UiVolume") +ambienceVolume = NodePath("Background/Rows/AmbienceVolumeRow/AmbienceVolume") +musicVolumeLabel = NodePath("Background/Rows/MusicVolumeRow/MusicVolumeValue") +sfxVolumeLabel = NodePath("Background/Rows/SfxVolumeRow/SfxVolumeValue") +uiVolumeLabel = NodePath("Background/Rows/UiVolumeRow/UiVolumeValue") +ambienceVolumeLabel = NodePath("Background/Rows/AmbienceVolumeRow/AmbienceVolumeValue") +musicVolumeRow = NodePath("Background/Rows/MusicVolumeRow") +sfxVolumeRow = NodePath("Background/Rows/SfxVolumeRow") +uiVolumeRow = NodePath("Background/Rows/UiVolumeRow") +ambienceVolumeRow = NodePath("Background/Rows/AmbienceVolumeRow") +close = NodePath("Background/Close") + +[node name="Background" type="TextureRect" parent="."] +layout_mode = 2 +mouse_filter = 0 +script = ExtResource("2_bg") +textureConfigKey = "screens.wonders.background" + +[node name="Rows" type="VBoxContainer" parent="Background"] +layout_mode = 0 +offset_left = 64.0 +offset_top = 68.0 +offset_right = 964.0 +offset_bottom = 716.0 +mouse_filter = 0 +theme_override_constants/separation = 8 +alignment = 1 + +[node name="GameHeading" type="Label" parent="Background/Rows"] +layout_mode = 2 +mouse_filter = 0 +theme_override_colors/font_color = Color(0.129412, 0.0980392, 0.054902, 1) theme_override_font_sizes/font_size = 18 -text = "Game" +text = "GAME" -[node name="GameGrid" type="GridContainer" parent="Margin/Panel/Rows"] +[node name="GameGrid" type="GridContainer" parent="Background/Rows"] layout_mode = 2 +size_flags_horizontal = 3 +mouse_filter = 0 columns = 3 -column_min_width = 220 +column_min_width = 290 -[node name="PromptForResearch" type="CheckBox" parent="Margin/Panel/Rows/GameGrid"] +[node name="PromptForResearch" type="CheckBox" parent="Background/Rows/GameGrid"] layout_mode = 2 +size_flags_horizontal = 0 +mouse_filter = 0 toggle_mode = true -script = ExtResource("2_checkbox") +script = ExtResource("3_checkbox") Text = "Ask which tech to research" FontSize = 13 +textPosition = 1 -[node name="UnitsHeading" type="Label" parent="Margin/Panel/Rows"] +[node name="UnitsHeading" type="Label" parent="Background/Rows"] layout_mode = 2 +mouse_filter = 0 +theme_override_colors/font_color = Color(0.129412, 0.0980392, 0.054902, 1) theme_override_font_sizes/font_size = 18 -text = "Units" +text = "UNITS" -[node name="UnitsNote" type="Label" parent="Margin/Panel/Rows"] +[node name="UnitsGrid" type="GridContainer" parent="Background/Rows"] layout_mode = 2 -theme_override_font_sizes/font_size = 12 +size_flags_horizontal = 3 +mouse_filter = 0 +columns = 3 +column_min_width = 290 + +[node name="UnitsNote" type="Label" parent="Background/Rows/UnitsGrid"] +layout_mode = 2 +mouse_filter = 0 +theme_override_colors/font_color = Color(0.129412, 0.0980392, 0.054902, 1) +theme_override_font_sizes/font_size = 13 text = "No unit options available yet." -autowrap_mode = 2 -[node name="AudioHeading" type="Label" parent="Margin/Panel/Rows"] +[node name="AudioHeading" type="Label" parent="Background/Rows"] layout_mode = 2 +mouse_filter = 0 +theme_override_colors/font_color = Color(0.129412, 0.0980392, 0.054902, 1) theme_override_font_sizes/font_size = 18 -text = "Audio" +text = "AUDIO" -[node name="MusicVolumeRow" type="HBoxContainer" parent="Margin/Panel/Rows"] +[node name="MusicVolumeRow" type="HBoxContainer" parent="Background/Rows"] layout_mode = 2 +mouse_filter = 0 theme_override_constants/separation = 12 -[node name="MusicVolumeLabel" type="Label" parent="Margin/Panel/Rows/MusicVolumeRow"] -custom_minimum_size = Vector2(110, 0) +[node name="MusicVolumeLabel" type="Label" parent="Background/Rows/MusicVolumeRow"] +custom_minimum_size = Vector2(140, 0) layout_mode = 2 +size_flags_vertical = 4 +mouse_filter = 0 +theme_override_colors/font_color = Color(0.129412, 0.0980392, 0.054902, 1) +theme_override_font_sizes/font_size = 13 text = "Music" -[node name="MusicVolume" type="HSlider" parent="Margin/Panel/Rows/MusicVolumeRow"] -custom_minimum_size = Vector2(220, 0) +[node name="MusicVolume" type="HSlider" parent="Background/Rows/MusicVolumeRow"] +custom_minimum_size = Vector2(0, 20) layout_mode = 2 size_flags_horizontal = 3 +size_flags_vertical = 4 +mouse_filter = 0 max_value = 100.0 step = 1.0 value = 100.0 -script = ExtResource("3_slider") +script = ExtResource("4_slider") metadata/_custom_type_script = "uid://clv4mgavtlrul" -[node name="MusicVolumeValue" type="Label" parent="Margin/Panel/Rows/MusicVolumeRow"] -custom_minimum_size = Vector2(50, 0) +[node name="MusicVolumeValue" type="Label" parent="Background/Rows/MusicVolumeRow"] +custom_minimum_size = Vector2(52, 0) layout_mode = 2 +size_flags_vertical = 4 +mouse_filter = 0 +theme_override_colors/font_color = Color(0.129412, 0.0980392, 0.054902, 1) +theme_override_font_sizes/font_size = 13 text = "100%" horizontal_alignment = 2 -[node name="SfxVolumeRow" type="HBoxContainer" parent="Margin/Panel/Rows"] +[node name="SfxVolumeRow" type="HBoxContainer" parent="Background/Rows"] layout_mode = 2 +mouse_filter = 0 theme_override_constants/separation = 12 -[node name="SfxVolumeLabel" type="Label" parent="Margin/Panel/Rows/SfxVolumeRow"] -custom_minimum_size = Vector2(110, 0) +[node name="SfxVolumeLabel" type="Label" parent="Background/Rows/SfxVolumeRow"] +custom_minimum_size = Vector2(140, 0) layout_mode = 2 +size_flags_vertical = 4 +mouse_filter = 0 +theme_override_colors/font_color = Color(0.129412, 0.0980392, 0.054902, 1) +theme_override_font_sizes/font_size = 13 text = "Sound effects" -[node name="SfxVolume" type="HSlider" parent="Margin/Panel/Rows/SfxVolumeRow"] -custom_minimum_size = Vector2(220, 0) +[node name="SfxVolume" type="HSlider" parent="Background/Rows/SfxVolumeRow"] +custom_minimum_size = Vector2(0, 20) layout_mode = 2 size_flags_horizontal = 3 +size_flags_vertical = 4 +mouse_filter = 0 max_value = 100.0 step = 1.0 value = 100.0 -script = ExtResource("3_slider") +script = ExtResource("4_slider") metadata/_custom_type_script = "uid://clv4mgavtlrul" -[node name="SfxVolumeValue" type="Label" parent="Margin/Panel/Rows/SfxVolumeRow"] -custom_minimum_size = Vector2(50, 0) +[node name="SfxVolumeValue" type="Label" parent="Background/Rows/SfxVolumeRow"] +custom_minimum_size = Vector2(52, 0) layout_mode = 2 +size_flags_vertical = 4 +mouse_filter = 0 +theme_override_colors/font_color = Color(0.129412, 0.0980392, 0.054902, 1) +theme_override_font_sizes/font_size = 13 text = "100%" horizontal_alignment = 2 -[node name="UiVolumeRow" type="HBoxContainer" parent="Margin/Panel/Rows"] +[node name="UiVolumeRow" type="HBoxContainer" parent="Background/Rows"] layout_mode = 2 +mouse_filter = 0 theme_override_constants/separation = 12 -[node name="UiVolumeLabel" type="Label" parent="Margin/Panel/Rows/UiVolumeRow"] -custom_minimum_size = Vector2(110, 0) +[node name="UiVolumeLabel" type="Label" parent="Background/Rows/UiVolumeRow"] +custom_minimum_size = Vector2(140, 0) layout_mode = 2 +size_flags_vertical = 4 +mouse_filter = 0 +theme_override_colors/font_color = Color(0.129412, 0.0980392, 0.054902, 1) +theme_override_font_sizes/font_size = 13 text = "Interface" -[node name="UiVolume" type="HSlider" parent="Margin/Panel/Rows/UiVolumeRow"] -custom_minimum_size = Vector2(220, 0) +[node name="UiVolume" type="HSlider" parent="Background/Rows/UiVolumeRow"] +custom_minimum_size = Vector2(0, 20) layout_mode = 2 size_flags_horizontal = 3 +size_flags_vertical = 4 +mouse_filter = 0 max_value = 100.0 step = 1.0 value = 100.0 -script = ExtResource("3_slider") +script = ExtResource("4_slider") metadata/_custom_type_script = "uid://clv4mgavtlrul" -[node name="UiVolumeValue" type="Label" parent="Margin/Panel/Rows/UiVolumeRow"] -custom_minimum_size = Vector2(50, 0) +[node name="UiVolumeValue" type="Label" parent="Background/Rows/UiVolumeRow"] +custom_minimum_size = Vector2(52, 0) layout_mode = 2 +size_flags_vertical = 4 +mouse_filter = 0 +theme_override_colors/font_color = Color(0.129412, 0.0980392, 0.054902, 1) +theme_override_font_sizes/font_size = 13 text = "100%" horizontal_alignment = 2 -[node name="AmbienceVolumeRow" type="HBoxContainer" parent="Margin/Panel/Rows"] +[node name="AmbienceVolumeRow" type="HBoxContainer" parent="Background/Rows"] layout_mode = 2 +mouse_filter = 0 theme_override_constants/separation = 12 -[node name="AmbienceVolumeLabel" type="Label" parent="Margin/Panel/Rows/AmbienceVolumeRow"] -custom_minimum_size = Vector2(110, 0) +[node name="AmbienceVolumeLabel" type="Label" parent="Background/Rows/AmbienceVolumeRow"] +custom_minimum_size = Vector2(140, 0) layout_mode = 2 +size_flags_vertical = 4 +mouse_filter = 0 +theme_override_colors/font_color = Color(0.129412, 0.0980392, 0.054902, 1) +theme_override_font_sizes/font_size = 13 text = "Ambience" -[node name="AmbienceVolume" type="HSlider" parent="Margin/Panel/Rows/AmbienceVolumeRow"] -custom_minimum_size = Vector2(220, 0) +[node name="AmbienceVolume" type="HSlider" parent="Background/Rows/AmbienceVolumeRow"] +custom_minimum_size = Vector2(0, 20) layout_mode = 2 size_flags_horizontal = 3 +size_flags_vertical = 4 +mouse_filter = 0 max_value = 100.0 step = 1.0 value = 100.0 -script = ExtResource("3_slider") +script = ExtResource("4_slider") metadata/_custom_type_script = "uid://clv4mgavtlrul" -[node name="AmbienceVolumeValue" type="Label" parent="Margin/Panel/Rows/AmbienceVolumeRow"] -custom_minimum_size = Vector2(50, 0) +[node name="AmbienceVolumeValue" type="Label" parent="Background/Rows/AmbienceVolumeRow"] +custom_minimum_size = Vector2(52, 0) layout_mode = 2 +size_flags_vertical = 4 +mouse_filter = 0 +theme_override_colors/font_color = Color(0.129412, 0.0980392, 0.054902, 1) +theme_override_font_sizes/font_size = 13 text = "100%" horizontal_alignment = 2 -[node name="FooterSeparator" type="HSeparator" parent="Margin/Panel/Rows"] -layout_mode = 2 - -[node name="Footer" type="HBoxContainer" parent="Margin/Panel/Rows"] -layout_mode = 2 -alignment = 2 - -[node name="Close" type="TextureButton" parent="Margin/Panel/Rows/Footer"] -custom_minimum_size = Vector2(72, 19) -layout_mode = 2 -script = ExtResource("4_close") +[node name="Close" type="TextureButton" parent="Background"] +layout_mode = 0 +offset_left = 952.0 +offset_top = 720.0 +offset_right = 1024.0 +offset_bottom = 768.0 +mouse_filter = 0 +script = ExtResource("5_close") \ No newline at end of file From 88b377dd026a2aa498de19ca18639caa260855e4 Mon Sep 17 00:00:00 2001 From: Billytifft Date: Sun, 4 Oct 2026 21:01:36 -0600 Subject: [PATCH 10/13] Split preferences into three vertical sections Game, Units and Audio were stacked in one vertically centred box, which left them bunched in the middle of the panel. Give each section its own container that expands to fill an equal third of the inner frame, with the section contents centred inside its band. --- C7/UIElements/preferences.tscn | 94 +++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 37 deletions(-) diff --git a/C7/UIElements/preferences.tscn b/C7/UIElements/preferences.tscn index d174712ba..d4bd5f53f 100644 --- a/C7/UIElements/preferences.tscn +++ b/C7/UIElements/preferences.tscn @@ -15,19 +15,19 @@ grow_vertical = 2 mouse_filter = 0 script = ExtResource("1_prefs") background = NodePath("Background") -promptForResearch = NodePath("Background/Rows/GameGrid/PromptForResearch") -musicVolume = NodePath("Background/Rows/MusicVolumeRow/MusicVolume") -sfxVolume = NodePath("Background/Rows/SfxVolumeRow/SfxVolume") -uiVolume = NodePath("Background/Rows/UiVolumeRow/UiVolume") -ambienceVolume = NodePath("Background/Rows/AmbienceVolumeRow/AmbienceVolume") -musicVolumeLabel = NodePath("Background/Rows/MusicVolumeRow/MusicVolumeValue") -sfxVolumeLabel = NodePath("Background/Rows/SfxVolumeRow/SfxVolumeValue") -uiVolumeLabel = NodePath("Background/Rows/UiVolumeRow/UiVolumeValue") -ambienceVolumeLabel = NodePath("Background/Rows/AmbienceVolumeRow/AmbienceVolumeValue") -musicVolumeRow = NodePath("Background/Rows/MusicVolumeRow") -sfxVolumeRow = NodePath("Background/Rows/SfxVolumeRow") -uiVolumeRow = NodePath("Background/Rows/UiVolumeRow") -ambienceVolumeRow = NodePath("Background/Rows/AmbienceVolumeRow") +promptForResearch = NodePath("Background/Rows/GameSection/GameGrid/PromptForResearch") +musicVolume = NodePath("Background/Rows/AudioSection/MusicVolumeRow/MusicVolume") +sfxVolume = NodePath("Background/Rows/AudioSection/SfxVolumeRow/SfxVolume") +uiVolume = NodePath("Background/Rows/AudioSection/UiVolumeRow/UiVolume") +ambienceVolume = NodePath("Background/Rows/AudioSection/AmbienceVolumeRow/AmbienceVolume") +musicVolumeLabel = NodePath("Background/Rows/AudioSection/MusicVolumeRow/MusicVolumeValue") +sfxVolumeLabel = NodePath("Background/Rows/AudioSection/SfxVolumeRow/SfxVolumeValue") +uiVolumeLabel = NodePath("Background/Rows/AudioSection/UiVolumeRow/UiVolumeValue") +ambienceVolumeLabel = NodePath("Background/Rows/AudioSection/AmbienceVolumeRow/AmbienceVolumeValue") +musicVolumeRow = NodePath("Background/Rows/AudioSection/MusicVolumeRow") +sfxVolumeRow = NodePath("Background/Rows/AudioSection/SfxVolumeRow") +uiVolumeRow = NodePath("Background/Rows/AudioSection/UiVolumeRow") +ambienceVolumeRow = NodePath("Background/Rows/AudioSection/AmbienceVolumeRow") close = NodePath("Background/Close") [node name="Background" type="TextureRect" parent="."] @@ -43,24 +43,44 @@ offset_top = 68.0 offset_right = 964.0 offset_bottom = 716.0 mouse_filter = 0 -theme_override_constants/separation = 8 +theme_override_constants/separation = 0 + +[node name="GameSection" type="VBoxContainer" parent="Background/Rows"] +layout_mode = 2 +size_flags_vertical = 3 +mouse_filter = 0 +theme_override_constants/separation = 6 +alignment = 1 + +[node name="UnitsSection" type="VBoxContainer" parent="Background/Rows"] +layout_mode = 2 +size_flags_vertical = 3 +mouse_filter = 0 +theme_override_constants/separation = 6 +alignment = 1 + +[node name="AudioSection" type="VBoxContainer" parent="Background/Rows"] +layout_mode = 2 +size_flags_vertical = 3 +mouse_filter = 0 +theme_override_constants/separation = 6 alignment = 1 -[node name="GameHeading" type="Label" parent="Background/Rows"] +[node name="GameHeading" type="Label" parent="Background/Rows/GameSection"] layout_mode = 2 mouse_filter = 0 theme_override_colors/font_color = Color(0.129412, 0.0980392, 0.054902, 1) theme_override_font_sizes/font_size = 18 text = "GAME" -[node name="GameGrid" type="GridContainer" parent="Background/Rows"] +[node name="GameGrid" type="GridContainer" parent="Background/Rows/GameSection"] layout_mode = 2 size_flags_horizontal = 3 mouse_filter = 0 columns = 3 column_min_width = 290 -[node name="PromptForResearch" type="CheckBox" parent="Background/Rows/GameGrid"] +[node name="PromptForResearch" type="CheckBox" parent="Background/Rows/GameSection/GameGrid"] layout_mode = 2 size_flags_horizontal = 0 mouse_filter = 0 @@ -70,40 +90,40 @@ Text = "Ask which tech to research" FontSize = 13 textPosition = 1 -[node name="UnitsHeading" type="Label" parent="Background/Rows"] +[node name="UnitsHeading" type="Label" parent="Background/Rows/UnitsSection"] layout_mode = 2 mouse_filter = 0 theme_override_colors/font_color = Color(0.129412, 0.0980392, 0.054902, 1) theme_override_font_sizes/font_size = 18 text = "UNITS" -[node name="UnitsGrid" type="GridContainer" parent="Background/Rows"] +[node name="UnitsGrid" type="GridContainer" parent="Background/Rows/UnitsSection"] layout_mode = 2 size_flags_horizontal = 3 mouse_filter = 0 columns = 3 column_min_width = 290 -[node name="UnitsNote" type="Label" parent="Background/Rows/UnitsGrid"] +[node name="UnitsNote" type="Label" parent="Background/Rows/UnitsSection/UnitsGrid"] layout_mode = 2 mouse_filter = 0 theme_override_colors/font_color = Color(0.129412, 0.0980392, 0.054902, 1) theme_override_font_sizes/font_size = 13 text = "No unit options available yet." -[node name="AudioHeading" type="Label" parent="Background/Rows"] +[node name="AudioHeading" type="Label" parent="Background/Rows/AudioSection"] layout_mode = 2 mouse_filter = 0 theme_override_colors/font_color = Color(0.129412, 0.0980392, 0.054902, 1) theme_override_font_sizes/font_size = 18 text = "AUDIO" -[node name="MusicVolumeRow" type="HBoxContainer" parent="Background/Rows"] +[node name="MusicVolumeRow" type="HBoxContainer" parent="Background/Rows/AudioSection"] layout_mode = 2 mouse_filter = 0 theme_override_constants/separation = 12 -[node name="MusicVolumeLabel" type="Label" parent="Background/Rows/MusicVolumeRow"] +[node name="MusicVolumeLabel" type="Label" parent="Background/Rows/AudioSection/MusicVolumeRow"] custom_minimum_size = Vector2(140, 0) layout_mode = 2 size_flags_vertical = 4 @@ -112,7 +132,7 @@ theme_override_colors/font_color = Color(0.129412, 0.0980392, 0.054902, 1) theme_override_font_sizes/font_size = 13 text = "Music" -[node name="MusicVolume" type="HSlider" parent="Background/Rows/MusicVolumeRow"] +[node name="MusicVolume" type="HSlider" parent="Background/Rows/AudioSection/MusicVolumeRow"] custom_minimum_size = Vector2(0, 20) layout_mode = 2 size_flags_horizontal = 3 @@ -124,7 +144,7 @@ value = 100.0 script = ExtResource("4_slider") metadata/_custom_type_script = "uid://clv4mgavtlrul" -[node name="MusicVolumeValue" type="Label" parent="Background/Rows/MusicVolumeRow"] +[node name="MusicVolumeValue" type="Label" parent="Background/Rows/AudioSection/MusicVolumeRow"] custom_minimum_size = Vector2(52, 0) layout_mode = 2 size_flags_vertical = 4 @@ -134,12 +154,12 @@ theme_override_font_sizes/font_size = 13 text = "100%" horizontal_alignment = 2 -[node name="SfxVolumeRow" type="HBoxContainer" parent="Background/Rows"] +[node name="SfxVolumeRow" type="HBoxContainer" parent="Background/Rows/AudioSection"] layout_mode = 2 mouse_filter = 0 theme_override_constants/separation = 12 -[node name="SfxVolumeLabel" type="Label" parent="Background/Rows/SfxVolumeRow"] +[node name="SfxVolumeLabel" type="Label" parent="Background/Rows/AudioSection/SfxVolumeRow"] custom_minimum_size = Vector2(140, 0) layout_mode = 2 size_flags_vertical = 4 @@ -148,7 +168,7 @@ theme_override_colors/font_color = Color(0.129412, 0.0980392, 0.054902, 1) theme_override_font_sizes/font_size = 13 text = "Sound effects" -[node name="SfxVolume" type="HSlider" parent="Background/Rows/SfxVolumeRow"] +[node name="SfxVolume" type="HSlider" parent="Background/Rows/AudioSection/SfxVolumeRow"] custom_minimum_size = Vector2(0, 20) layout_mode = 2 size_flags_horizontal = 3 @@ -160,7 +180,7 @@ value = 100.0 script = ExtResource("4_slider") metadata/_custom_type_script = "uid://clv4mgavtlrul" -[node name="SfxVolumeValue" type="Label" parent="Background/Rows/SfxVolumeRow"] +[node name="SfxVolumeValue" type="Label" parent="Background/Rows/AudioSection/SfxVolumeRow"] custom_minimum_size = Vector2(52, 0) layout_mode = 2 size_flags_vertical = 4 @@ -170,12 +190,12 @@ theme_override_font_sizes/font_size = 13 text = "100%" horizontal_alignment = 2 -[node name="UiVolumeRow" type="HBoxContainer" parent="Background/Rows"] +[node name="UiVolumeRow" type="HBoxContainer" parent="Background/Rows/AudioSection"] layout_mode = 2 mouse_filter = 0 theme_override_constants/separation = 12 -[node name="UiVolumeLabel" type="Label" parent="Background/Rows/UiVolumeRow"] +[node name="UiVolumeLabel" type="Label" parent="Background/Rows/AudioSection/UiVolumeRow"] custom_minimum_size = Vector2(140, 0) layout_mode = 2 size_flags_vertical = 4 @@ -184,7 +204,7 @@ theme_override_colors/font_color = Color(0.129412, 0.0980392, 0.054902, 1) theme_override_font_sizes/font_size = 13 text = "Interface" -[node name="UiVolume" type="HSlider" parent="Background/Rows/UiVolumeRow"] +[node name="UiVolume" type="HSlider" parent="Background/Rows/AudioSection/UiVolumeRow"] custom_minimum_size = Vector2(0, 20) layout_mode = 2 size_flags_horizontal = 3 @@ -196,7 +216,7 @@ value = 100.0 script = ExtResource("4_slider") metadata/_custom_type_script = "uid://clv4mgavtlrul" -[node name="UiVolumeValue" type="Label" parent="Background/Rows/UiVolumeRow"] +[node name="UiVolumeValue" type="Label" parent="Background/Rows/AudioSection/UiVolumeRow"] custom_minimum_size = Vector2(52, 0) layout_mode = 2 size_flags_vertical = 4 @@ -206,12 +226,12 @@ theme_override_font_sizes/font_size = 13 text = "100%" horizontal_alignment = 2 -[node name="AmbienceVolumeRow" type="HBoxContainer" parent="Background/Rows"] +[node name="AmbienceVolumeRow" type="HBoxContainer" parent="Background/Rows/AudioSection"] layout_mode = 2 mouse_filter = 0 theme_override_constants/separation = 12 -[node name="AmbienceVolumeLabel" type="Label" parent="Background/Rows/AmbienceVolumeRow"] +[node name="AmbienceVolumeLabel" type="Label" parent="Background/Rows/AudioSection/AmbienceVolumeRow"] custom_minimum_size = Vector2(140, 0) layout_mode = 2 size_flags_vertical = 4 @@ -220,7 +240,7 @@ theme_override_colors/font_color = Color(0.129412, 0.0980392, 0.054902, 1) theme_override_font_sizes/font_size = 13 text = "Ambience" -[node name="AmbienceVolume" type="HSlider" parent="Background/Rows/AmbienceVolumeRow"] +[node name="AmbienceVolume" type="HSlider" parent="Background/Rows/AudioSection/AmbienceVolumeRow"] custom_minimum_size = Vector2(0, 20) layout_mode = 2 size_flags_horizontal = 3 @@ -232,7 +252,7 @@ value = 100.0 script = ExtResource("4_slider") metadata/_custom_type_script = "uid://clv4mgavtlrul" -[node name="AmbienceVolumeValue" type="Label" parent="Background/Rows/AmbienceVolumeRow"] +[node name="AmbienceVolumeValue" type="Label" parent="Background/Rows/AudioSection/AmbienceVolumeRow"] custom_minimum_size = Vector2(52, 0) layout_mode = 2 size_flags_vertical = 4 From 35336ae7f119dd90b4985d1ca67352246874f99c Mon Sep 17 00:00:00 2001 From: Billytifft Date: Sun, 4 Oct 2026 21:13:07 -0600 Subject: [PATCH 11/13] Capitalise preferences and audio ini keys Review feedback: use the same nameof convention as C7Settings.LastGame so section and key names are consistent, and accept the minor breakage on a brand new feature. Sections/keys become [Audio] MusicVolume, SfxAudioVolume, UiAudioVolume, AmbienceAudioVolume and [Preferences] PromptForResearch. ConfigureVolume now reads through GetSettingsValueOrDefault. A missing key returns null from GetSettingValue, so int.Parse threw ArgumentNullException, which the catch (ApplicationException) did not cover -- it escaped _Ready and left the sfx/ui/ambience players uninitialised. Unset volumes now fall back to 100 instead. Also drop eleven dead `= null` lines from main_menu.tscn; MenuButtonContainer exposes those as plain properties, not exports, so Godot ignored them. --- C7/Audio/AudioManager.cs | 26 +++++++++++++------------- C7/PreferencesSettings.cs | 23 ++++++++--------------- C7/UIElements/MainMenu/main_menu.tscn | 11 ----------- C7/UIElements/preferences.tscn | 2 +- C7Engine/C7Settings.cs | 8 ++++++++ 5 files changed, 30 insertions(+), 40 deletions(-) diff --git a/C7/Audio/AudioManager.cs b/C7/Audio/AudioManager.cs index 792b34e94..53c140db6 100644 --- a/C7/Audio/AudioManager.cs +++ b/C7/Audio/AudioManager.cs @@ -19,8 +19,6 @@ public partial class AudioManager : Node { private PolyphonicAudioPlayer _polyUiAudioPlayer; private PolyphonicAudioPlayer _polyAmbienceAudioPlayer; - private static string AudioSettingsSection = "audio"; - public static string MusicBus = "Music"; public static string SfxAudioBus = "Sfx"; public static string UIAudioBus = "UI"; @@ -33,10 +31,10 @@ public partial class AudioManager : Node { // Maps a bus to the settings key holding its volume. Public so settings UI // can drive a bus without repeating the magic strings. public static readonly Dictionary BusVolumeKeys = new() { - { MusicBus, "musicVolume" }, - { SfxAudioBus, "sfxAudioVolume" }, - { UIAudioBus, "uiAudioVolume" }, - { AmbienceAudioBus, "ambienceAudioVolume" }, + { MusicBus, C7Settings.Audio.MusicVolume }, + { SfxAudioBus, C7Settings.Audio.SfxAudioVolume }, + { UIAudioBus, C7Settings.Audio.UiAudioVolume }, + { AmbienceAudioBus, C7Settings.Audio.AmbienceAudioVolume }, }; @@ -44,22 +42,24 @@ public partial class AudioManager : Node { public override void _Ready() { log = LogManager.ForContext(); - musicEnabled = ConfigureVolume("musicVolume", MusicBus); + musicEnabled = ConfigureVolume(C7Settings.Audio.MusicVolume, MusicBus); _polyMusicPlayer = new PolyphonicAudioPlayer(musicPlayer, 2); - sfxAudioEnabled = ConfigureVolume("sfxAudioVolume", SfxAudioBus); + sfxAudioEnabled = ConfigureVolume(C7Settings.Audio.SfxAudioVolume, SfxAudioBus); _polySfxAudioPlayer = new PolyphonicAudioPlayer(sfxAudioPlayer, 32); - uiAudioEnabled = ConfigureVolume("uiAudioVolume", UIAudioBus); + uiAudioEnabled = ConfigureVolume(C7Settings.Audio.UiAudioVolume, UIAudioBus); _polyUiAudioPlayer = new PolyphonicAudioPlayer(uiAudioPlayer, 8); - ambienceAudioEnabled = ConfigureVolume("ambienceAudioVolume", AmbienceAudioBus); + ambienceAudioEnabled = ConfigureVolume(C7Settings.Audio.AmbienceAudioVolume, AmbienceAudioBus); _polyAmbienceAudioPlayer = new PolyphonicAudioPlayer(ambienceAudioPlayer, 8); } private bool ConfigureVolume(string volumeKey, string audioBus) { try { - string volume = C7Settings.GetSettingValue(AudioSettingsSection, volumeKey); + // A missing key means the user never set it, so fall back to full + // volume rather than letting int.Parse throw on null. + string volume = C7Settings.GetSettingsValueOrDefault(C7Settings.Audio.SectionName, volumeKey, "100"); float volumeDb = LogicalVolumeAsDecibel(int.Parse(volume)); if (volumeDb == float.MinValue) { @@ -136,7 +136,7 @@ public int GetBusVolume(string bus) { log.Warning("No volume setting known for bus {bus}", bus); return -1; } - string stored = C7Settings.GetSettingsValueOrDefault(AudioSettingsSection, key, "100"); + string stored = C7Settings.GetSettingsValueOrDefault(C7Settings.Audio.SectionName, key, "100"); return int.TryParse(stored, out int volume) ? volume : 100; } @@ -146,7 +146,7 @@ public void SetBusVolume(string bus, int volume) { return; } volume = Mathf.Clamp(volume, 0, 100); - C7Settings.SetValue(AudioSettingsSection, key, volume.ToString()); + C7Settings.SetValue(C7Settings.Audio.SectionName, key, volume.ToString()); C7Settings.SaveSettings(); SetBusVolumeDb(bus, LogicalVolumeAsDecibel(volume)); } diff --git a/C7/PreferencesSettings.cs b/C7/PreferencesSettings.cs index b21022203..8fcd85bb0 100644 --- a/C7/PreferencesSettings.cs +++ b/C7/PreferencesSettings.cs @@ -1,30 +1,23 @@ using C7Engine; -using Godot; namespace C7.UIElements; /// /// User preferences, stored outside the save file so they stay stable across -/// games and scenarios. Not yet surfaced anywhere in the UI. +/// games and scenarios. Surfaced by the Preferences scene, reachable from the +/// main menu, the in-game GameMenu, and Ctrl-P. /// public static class PreferencesSettings { - public static class Game { - // Lower case on purpose: ini-parser treats section and key names as - // case-sensitive, and existing C7.ini files already use these names. - public const string SectionName = "preferences"; - public const string PromptForResearch = "promptForResearch"; - public const bool DefaultPromptForResearch = true; - } + public const string SectionName = "Preferences"; + public const string PromptForResearch = nameof(PromptForResearch); + public const bool DefaultPromptForResearch = true; public static bool GetPromptForResearch() { - return C7Settings.GetBoolOrDefault( - Game.SectionName, - Game.PromptForResearch, - Game.DefaultPromptForResearch); + return C7Settings.GetBoolOrDefault(SectionName, PromptForResearch, DefaultPromptForResearch); } public static void SetPromptForResearch(bool value) { - C7Settings.SetBool(Game.SectionName, Game.PromptForResearch, value); + C7Settings.SetBool(SectionName, PromptForResearch, value); C7Settings.SaveSettings(); } -} +} \ No newline at end of file diff --git a/C7/UIElements/MainMenu/main_menu.tscn b/C7/UIElements/MainMenu/main_menu.tscn index 3d83f2de2..d87fc7a9d 100644 --- a/C7/UIElements/MainMenu/main_menu.tscn +++ b/C7/UIElements/MainMenu/main_menu.tscn @@ -148,17 +148,6 @@ horizontal_alignment = 1 layout_mode = 2 theme_override_constants/separation = 15 script = ExtResource("3_t1nuq") -NewGame = null -QuickStart = null -Tutorial = null -LoadGame = null -LoadScenario = null -HallOfFame = null -ToggleGraphics = null -Preferences = null -AudioPreferences = null -Credits = null -Exit = null [node name="MarginContainer" type="MarginContainer" parent="Control/Background"] layout_mode = 1 diff --git a/C7/UIElements/preferences.tscn b/C7/UIElements/preferences.tscn index d4bd5f53f..2feff5e88 100644 --- a/C7/UIElements/preferences.tscn +++ b/C7/UIElements/preferences.tscn @@ -86,7 +86,7 @@ size_flags_horizontal = 0 mouse_filter = 0 toggle_mode = true script = ExtResource("3_checkbox") -Text = "Ask which tech to research" +Text = "Ask which tech to research next" FontSize = 13 textPosition = 1 diff --git a/C7Engine/C7Settings.cs b/C7Engine/C7Settings.cs index 40384bd2e..312bbabcf 100644 --- a/C7Engine/C7Settings.cs +++ b/C7Engine/C7Settings.cs @@ -9,6 +9,14 @@ public class C7Settings { private const string SETTINGS_FILE_NAME = "C7.ini"; public static IniData settings; + public static class Audio { + public const string SectionName = nameof(Audio); + public const string MusicVolume = nameof(MusicVolume); + public const string SfxAudioVolume = nameof(SfxAudioVolume); + public const string UiAudioVolume = nameof(UiAudioVolume); + public const string AmbienceAudioVolume = nameof(AmbienceAudioVolume); + } + public static class LastGame { public const string SectionName = nameof(LastGame); public const string WorldSize = nameof(WorldSize); From 1e7f06f8afaf5f12dfc23277950ddf66aad62c82 Mon Sep 17 00:00:00 2001 From: Billytifft Date: Sun, 4 Oct 2026 21:26:11 -0600 Subject: [PATCH 12/13] Load checkbox textures in Ready, not the constructor All three Godot export jobs died during savepack with System.ApplicationException: Media path not found: Art/3checkboxes-USE.pcx at Civ3Checkbox..ctor() ... Civ3Checkbox.cs:15 reached through CreateManagedForGodotObjectScriptInstance, i.e. Godot instantiating the script while packing a scene. C7.ini is gitignored, so CI has no civ3InstallDir and useStandaloneMode defaults to false: the config resolves ui.checkbox.* to the real Civ3 PCX, which cannot be found without a Civ3 install. preferences.tscn instances a Civ3Checkbox, and main_menu.tscn and C7Game.tscn instance preferences.tscn, so this was the first scene to reach that constructor during export. Civ3Checkbox was the only control that loaded a texture outside _Ready. Civ3TextureRect, which main_menu's background has always used, loads in _Ready and was fine in CI, so move the three loads there too, ahead of the textureRect that consumes them. Runtime is unchanged: the same keys resolve in either mode, and standalone/textures.lua derives its config from the civ3 one with the paths rewritten to PNG. Also restore the trailing newline in PreferencesSettings.cs, which dotnet format flagged. --- C7/PreferencesSettings.cs | 2 +- C7/UIElements/Civ3Checkbox.cs | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/C7/PreferencesSettings.cs b/C7/PreferencesSettings.cs index 8fcd85bb0..81d19ef73 100644 --- a/C7/PreferencesSettings.cs +++ b/C7/PreferencesSettings.cs @@ -20,4 +20,4 @@ public static void SetPromptForResearch(bool value) { C7Settings.SetBool(SectionName, PromptForResearch, value); C7Settings.SaveSettings(); } -} \ No newline at end of file +} diff --git a/C7/UIElements/Civ3Checkbox.cs b/C7/UIElements/Civ3Checkbox.cs index 35904c0f4..c135631e9 100644 --- a/C7/UIElements/Civ3Checkbox.cs +++ b/C7/UIElements/Civ3Checkbox.cs @@ -12,14 +12,11 @@ public enum TextPosition { TextBelowIcon } - private Texture2D normalTexture = TextureLoader.Load("ui.checkbox.inactive"); + private Texture2D normalTexture; private Texture2D hoverTexture; private Texture2D pressedTexture; public Civ3Checkbox() { - normalTexture = TextureLoader.Load("ui.checkbox.inactive"); - hoverTexture = TextureLoader.Load("ui.checkbox.hover"); - pressedTexture = TextureLoader.Load("ui.checkbox.pressed"); } private string _text; @@ -78,6 +75,10 @@ public Civ3Checkbox(TextPosition textPosition = TextPosition.TextRightOfIcon) { } public override void _Ready() { + normalTexture = TextureLoader.Load("ui.checkbox.inactive"); + hoverTexture = TextureLoader.Load("ui.checkbox.hover"); + pressedTexture = TextureLoader.Load("ui.checkbox.pressed"); + fontColor = GetThemeColor("font_color", "Button"); hoverColor = GetThemeColor("font_hover_color", "Button"); pressedColor = GetThemeColor("font_pressed_color", "Button"); From 2b9baf781626e8d06d731f085615faa4ed4761ee Mon Sep 17 00:00:00 2001 From: Billytifft Date: Wed, 7 Oct 2026 16:41:56 -0600 Subject: [PATCH 13/13] Add Ctrl+P toggle for preferences and handle key input --- C7/Game.cs | 21 +++++++++++++++++++-- C7/UIElements/MainMenu/main_menu.tscn | 1 - 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/C7/Game.cs b/C7/Game.cs index b78d7ba6a..0c95f569a 100644 --- a/C7/Game.cs +++ b/C7/Game.cs @@ -585,7 +585,11 @@ public void OnLoadGame() { public void OnOpenPreferences() { popupOverlay.OnHidePopup(); - preferences.ShowPreferences(); + if (preferences.Visible) { + preferences.HidePreferences(); + } else { + preferences.ShowPreferences(); + } } public void OnResolved() { @@ -824,6 +828,15 @@ private void HandleKeyboardInput(InputEventKey eventKeyDown) { ToggleObserverMode(); } + if (eventKeyDown.Keycode == Godot.Key.P && eventKeyDown.IsCommandOrControlPressed()) { + if (preferences.Visible) { + preferences.HidePreferences(); + } else { + preferences.ShowPreferences(); + } + return; + } + if (eventKeyDown.Keycode == Godot.Key.F1) { EmitSignal(SignalName.ShowSpecificAdvisor, C7Action.ShowDomesticAdvisor); } @@ -998,7 +1011,11 @@ private void ProcessAction(string currentAction) { } if (currentAction == C7Action.OpenPreferences) { - preferences.ShowPreferences(); + if (preferences.Visible) { + preferences.HidePreferences(); + } else { + preferences.ShowPreferences(); + } return; } diff --git a/C7/UIElements/MainMenu/main_menu.tscn b/C7/UIElements/MainMenu/main_menu.tscn index d87fc7a9d..008d027fc 100644 --- a/C7/UIElements/MainMenu/main_menu.tscn +++ b/C7/UIElements/MainMenu/main_menu.tscn @@ -231,7 +231,6 @@ visible = false [node name="Preferences" parent="." instance=ExtResource("6_prefs")] visible = false -layout_mode = 3 [connection signal="pressed" from="Control/Background/PanelContainer/MarginContainer/NoCiv3Options/SetCiv3Home" to="." method="_on_SetCiv3Home_pressed"] [connection signal="pressed" from="Control/Background/PanelContainer/MarginContainer/NoCiv3Options/UseStandaloneMode" to="." method="UseStandaloneModePressed"]