Compare commits
5
Commits
b3ac8aec8f
..
v1.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a090f74d07 | ||
|
|
9ea7cd65e2 | ||
|
|
f68ed6b0ac | ||
|
|
f04a2a3661 | ||
|
|
9bd0483af9 |
No files matched your search
+22
-10
@@ -26,6 +26,11 @@ menu/play/status_mismatch = Incompatible versions: {0}
|
||||
menu/play/status_problems = Problems in the game composition: {0}
|
||||
menu/play/status_install = To install: {0}, to enable: {1}
|
||||
menu/load/empty = No saves yet
|
||||
menu/save/new_slot = + New save
|
||||
menu/load/hint = Click a save to load it
|
||||
menu/save/hint = Click a slot to save the game; a used slot is overwritten after a confirmation
|
||||
menu/play/hint_warming = The games' mods are being prepared in the background — starting will be quick afterwards
|
||||
menu/play/hint_ready = The games' mods are ready, starting takes a few seconds
|
||||
menu/load/legacy_group = Saves without a game
|
||||
menu/load/legacy_title = Save without a game
|
||||
menu/load/legacy_text = This save was made before game collections existed. Choose the game it belongs to — the file moves into that game's saves.
|
||||
@@ -44,6 +49,11 @@ settings/back = Back
|
||||
settings/audio = Audio
|
||||
settings/sounds = Sounds
|
||||
settings/music = Music
|
||||
settings/ambient = Ambience
|
||||
settings/interface_language = Interface language
|
||||
settings/section_window = Window
|
||||
settings/section_performance = Performance
|
||||
settings/saved_at_once = Changes are applied and saved at once
|
||||
settings/volume = Volume
|
||||
settings/language = Language
|
||||
settings/lang_ru = Russian
|
||||
@@ -55,18 +65,20 @@ settings/tab_display = Display
|
||||
settings/display = Display
|
||||
settings/window_mode = Window Mode
|
||||
settings/window_mode_windowed = Windowed
|
||||
settings/window_mode_fullscreen = Fullscreen
|
||||
settings/window_mode_borderless = Borderless
|
||||
settings/resolution = Resolution
|
||||
settings/window_mode_maximized = Maximized window
|
||||
settings/window_mode_borderless = Fullscreen (borderless)
|
||||
settings/window_mode_fullscreen = Fullscreen (exclusive)
|
||||
settings/resolution = Window size
|
||||
settings/resolution_fullscreen_hint = Fullscreen modes use the monitor resolution
|
||||
settings/monitor = Monitor
|
||||
settings/monitor_screen = Screen {0} ({1}x{2})
|
||||
settings/monitor_screen = Monitor {0} ({1}×{2})
|
||||
settings/monitor_screen_primary = Monitor {0} ({1}×{2}), primary
|
||||
settings/vsync = V-Sync
|
||||
settings/scale = Scale
|
||||
settings/scale_1x = 1x
|
||||
settings/scale_2x = 2x
|
||||
settings/scale_3x = 3x
|
||||
settings/scale_4x = 4x
|
||||
settings/tab_mods = Mod Settings
|
||||
settings/max_fps = Frame rate limit
|
||||
settings/max_fps_unlimited = Unlimited
|
||||
settings/max_fps_value = {0} FPS
|
||||
settings/display_reset = Reset display settings
|
||||
settings/tab_mods = Mods
|
||||
settings/mod_list_title = Mods
|
||||
settings/mods_no_settings = No mod settings available
|
||||
settings/mods_reset = Reset to Defaults
|
||||
+23
-11
@@ -26,6 +26,11 @@ menu/play/status_mismatch = Несовместимые версии: {0}
|
||||
menu/play/status_problems = Ошибки в составе игры: {0}
|
||||
menu/play/status_install = Нужно установить: {0}, включить: {1}
|
||||
menu/load/empty = Сохранений пока нет
|
||||
menu/save/new_slot = + Новое сохранение
|
||||
menu/load/hint = Нажмите на сохранение, чтобы загрузить его
|
||||
menu/save/hint = Нажмите на слот, чтобы сохранить игру; занятый слот перезапишется после подтверждения
|
||||
menu/play/hint_warming = Моды игр готовятся в фоне — запуск после этого будет быстрым
|
||||
menu/play/hint_ready = Моды игр готовы, запуск займёт несколько секунд
|
||||
menu/load/legacy_group = Сохранения без игры
|
||||
menu/load/legacy_title = Сохранение без игры
|
||||
menu/load/legacy_text = Это сохранение сделано до появления игр-коллекций. Выберите игру, к которой оно относится, — файл переедет в её сохранения.
|
||||
@@ -44,6 +49,11 @@ settings/back = Назад
|
||||
settings/audio = Аудио
|
||||
settings/sounds = Звуки
|
||||
settings/music = Музыка
|
||||
settings/ambient = Окружение
|
||||
settings/interface_language = Язык интерфейса
|
||||
settings/section_window = Окно
|
||||
settings/section_performance = Производительность
|
||||
settings/saved_at_once = Изменения применяются и сохраняются сразу
|
||||
settings/volume = Громкость
|
||||
settings/language = Язык
|
||||
settings/lang_ru = Русский
|
||||
@@ -54,19 +64,21 @@ settings/tab_audio = Звук
|
||||
settings/tab_display = Экран
|
||||
settings/display = Экран
|
||||
settings/window_mode = Режим окна
|
||||
settings/window_mode_windowed = Оконный
|
||||
settings/window_mode_fullscreen = Полный экран
|
||||
settings/window_mode_borderless = Без рамки
|
||||
settings/resolution = Разрешение
|
||||
settings/window_mode_windowed = В окне
|
||||
settings/window_mode_maximized = Окно на весь экран
|
||||
settings/window_mode_borderless = Полный экран (без рамки)
|
||||
settings/window_mode_fullscreen = Полный экран (эксклюзивный)
|
||||
settings/resolution = Размер окна
|
||||
settings/resolution_fullscreen_hint = В полноэкранных режимах используется разрешение монитора
|
||||
settings/monitor = Монитор
|
||||
settings/monitor_screen = Экран {0} ({1}x{2})
|
||||
settings/monitor_screen = Монитор {0} ({1}×{2})
|
||||
settings/monitor_screen_primary = Монитор {0} ({1}×{2}), основной
|
||||
settings/vsync = Вертикальная синхронизация
|
||||
settings/scale = Масштаб
|
||||
settings/scale_1x = 1x
|
||||
settings/scale_2x = 2x
|
||||
settings/scale_3x = 3x
|
||||
settings/scale_4x = 4x
|
||||
settings/tab_mods = Настройки модов
|
||||
settings/max_fps = Ограничение кадров
|
||||
settings/max_fps_unlimited = Без ограничения
|
||||
settings/max_fps_value = {0} кадров/с
|
||||
settings/display_reset = Сбросить настройки экрана
|
||||
settings/tab_mods = Моды
|
||||
settings/mod_list_title = Моды
|
||||
settings/mods_no_settings = Нет доступных настроек модов
|
||||
settings/mods_reset = Сбросить настройки
|
||||
+2
-1
@@ -14,6 +14,7 @@
|
||||
{"id": "save_dormancy", "activate": ["--itest=save_dormancy"]},
|
||||
{"id": "new_game", "activate": ["--itest=new_game"]},
|
||||
{"id": "hot_reload", "activate": ["--itest=hot_reload"]},
|
||||
{"id": "collection_edit", "activate": ["--itest=collection_edit"]}
|
||||
{"id": "collection_edit", "activate": ["--itest=collection_edit"]},
|
||||
{"id": "display_settings", "activate": ["--itest=display_settings"]}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
[gd_scene format=3]
|
||||
|
||||
[ext_resource type="Script" path="src/GamePickerPanel.cs" id="GamePickerScript"]
|
||||
|
||||
[node name="GamePickerPanel" type="Control" unique_id=352232428]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("GamePickerScript")
|
||||
|
||||
[node name="Backdrop" type="ColorRect" parent="." unique_id=1612952176]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
color = Color(0, 0, 0, 0.9)
|
||||
|
||||
[node name="Window" type="PanelContainer" parent="." unique_id=788680784]
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -520.0
|
||||
offset_top = -360.0
|
||||
offset_right = 520.0
|
||||
offset_bottom = 360.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Column" type="VBoxContainer" parent="Window" unique_id=1513362382]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 0
|
||||
|
||||
[node name="Header" type="MarginContainer" parent="Window/Column" unique_id=1901680926]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 24
|
||||
theme_override_constants/margin_top = 16
|
||||
theme_override_constants/margin_right = 16
|
||||
theme_override_constants/margin_bottom = 16
|
||||
|
||||
[node name="Row" type="HBoxContainer" parent="Window/Column/Header" unique_id=897579862]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 24
|
||||
|
||||
[node name="Title" type="Label" parent="Window/Column/Header/Row" unique_id=1589586692]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
text = "Choose a game"
|
||||
uppercase = true
|
||||
|
||||
[node name="Spacer" type="Control" parent="Window/Column/Header/Row" unique_id=1565293582]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="CloseButton" type="Button" parent="Window/Column/Header/Row" unique_id=392870380]
|
||||
layout_mode = 2
|
||||
custom_minimum_size = Vector2(38, 38)
|
||||
size_flags_vertical = 4
|
||||
focus_mode = 0
|
||||
text = "✕"
|
||||
|
||||
[node name="RuleHeader" type="ColorRect" parent="Window/Column" unique_id=1522041891]
|
||||
custom_minimum_size = Vector2(0, 1)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Body" type="MarginContainer" parent="Window/Column" unique_id=897575893]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/margin_left = 24
|
||||
theme_override_constants/margin_top = 20
|
||||
theme_override_constants/margin_right = 24
|
||||
theme_override_constants/margin_bottom = 20
|
||||
|
||||
[node name="Scroll" type="ScrollContainer" parent="Window/Column/Body" unique_id=1648576427]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
horizontal_scroll_mode = 0
|
||||
|
||||
[node name="List" type="VBoxContainer" parent="Window/Column/Body/Scroll" unique_id=355317885]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="RuleFooter" type="ColorRect" parent="Window/Column" unique_id=866559016]
|
||||
custom_minimum_size = Vector2(0, 1)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Footer" type="MarginContainer" parent="Window/Column" unique_id=508767107]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 24
|
||||
theme_override_constants/margin_top = 12
|
||||
theme_override_constants/margin_right = 24
|
||||
theme_override_constants/margin_bottom = 12
|
||||
|
||||
[node name="Row" type="HBoxContainer" parent="Window/Column/Footer" unique_id=1496801409]
|
||||
custom_minimum_size = Vector2(0, 38)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 12
|
||||
|
||||
[node name="Hint" type="Label" parent="Window/Column/Footer/Row" unique_id=1150953277]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 4
|
||||
@@ -1,84 +1,131 @@
|
||||
[gd_scene load_steps=3 format=3]
|
||||
[gd_scene format=3]
|
||||
|
||||
[ext_resource type="Script" path="src/SaveLoadMenu.cs" id="SaveLoadMenuScript"]
|
||||
[ext_resource type="Script" path="src/SaveSlotList.cs" id="SaveSlotListScript"]
|
||||
|
||||
[node name="SaveLoadMenu" type="Control"]
|
||||
[node name="SaveLoadMenu" type="Control" unique_id=1300922993]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 1
|
||||
script = ExtResource("SaveLoadMenuScript")
|
||||
|
||||
[node name="Bg" type="ColorRect" parent="."]
|
||||
[node name="Backdrop" type="ColorRect" parent="." unique_id=1957446002]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
color = Color(0, 0, 0, 0.65)
|
||||
color = Color(0, 0, 0, 0.9)
|
||||
|
||||
[node name="Panel" type="Panel" parent="."]
|
||||
[node name="Window" type="PanelContainer" parent="." unique_id=1214442353]
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -420.0
|
||||
offset_top = -280.0
|
||||
offset_right = 420.0
|
||||
offset_bottom = 280.0
|
||||
offset_left = -500.0
|
||||
offset_top = -350.0
|
||||
offset_right = 500.0
|
||||
offset_bottom = 350.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Margin" type="MarginContainer" parent="Panel"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/margin_left = 28
|
||||
theme_override_constants/margin_top = 20
|
||||
theme_override_constants/margin_right = 28
|
||||
theme_override_constants/margin_bottom = 20
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="Panel/Margin"]
|
||||
[node name="Column" type="VBoxContainer" parent="Window" unique_id=154632647]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 14
|
||||
theme_override_constants/separation = 0
|
||||
|
||||
[node name="Title" type="Label" parent="Panel/Margin/VBox"]
|
||||
[node name="Header" type="MarginContainer" parent="Window/Column" unique_id=255743212]
|
||||
layout_mode = 2
|
||||
text = "Save/Load"
|
||||
horizontal_alignment = 1
|
||||
theme_override_font_sizes/font_size = 24
|
||||
theme_override_constants/margin_left = 24
|
||||
theme_override_constants/margin_top = 16
|
||||
theme_override_constants/margin_right = 16
|
||||
theme_override_constants/margin_bottom = 16
|
||||
|
||||
[node name="GameSelect" type="OptionButton" parent="Panel/Margin/VBox"]
|
||||
[node name="Row" type="HBoxContainer" parent="Window/Column/Header" unique_id=1566510433]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 24
|
||||
|
||||
[node name="Scroll" type="ScrollContainer" parent="Panel/Margin/VBox"]
|
||||
[node name="Title" type="Label" parent="Window/Column/Header/Row" unique_id=1345868865]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
text = "Load"
|
||||
uppercase = true
|
||||
|
||||
[node name="GameSelect" type="OptionButton" parent="Window/Column/Header/Row" unique_id=1531297566]
|
||||
custom_minimum_size = Vector2(280, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
|
||||
[node name="Spacer" type="Control" parent="Window/Column/Header/Row" unique_id=739525391]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="CloseButton" type="Button" parent="Window/Column/Header/Row" unique_id=1063180782]
|
||||
layout_mode = 2
|
||||
custom_minimum_size = Vector2(38, 38)
|
||||
size_flags_vertical = 4
|
||||
focus_mode = 0
|
||||
text = "✕"
|
||||
|
||||
[node name="RuleHeader" type="ColorRect" parent="Window/Column" unique_id=1844886327]
|
||||
custom_minimum_size = Vector2(0, 1)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Body" type="MarginContainer" parent="Window/Column" unique_id=280231313]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/margin_left = 24
|
||||
theme_override_constants/margin_top = 20
|
||||
theme_override_constants/margin_right = 24
|
||||
theme_override_constants/margin_bottom = 20
|
||||
|
||||
[node name="Content" type="VBoxContainer" parent="Window/Column/Body" unique_id=1998936353]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 12
|
||||
|
||||
[node name="Scroll" type="ScrollContainer" parent="Window/Column/Body/Content" unique_id=131125275]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
custom_minimum_size = Vector2(0, 380)
|
||||
horizontal_scroll_mode = 0
|
||||
|
||||
[node name="SlotGrid" type="Control" parent="Panel/Margin/VBox/Scroll"]
|
||||
[node name="SlotGrid" type="Control" parent="Window/Column/Body/Content/Scroll" unique_id=702369365]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
script = ExtResource("SaveSlotListScript")
|
||||
ColumnCount = 3
|
||||
RowCount = 2
|
||||
SlotPadding = 14.0
|
||||
|
||||
[node name="EmptyLabel" type="Label" parent="Panel/Margin/VBox"]
|
||||
[node name="EmptyLabel" type="Label" parent="Window/Column/Body/Content" unique_id=1909778806]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
visible = false
|
||||
|
||||
[node name="BackBtn" type="Button" parent="Panel/Margin/VBox"]
|
||||
[node name="RuleFooter" type="ColorRect" parent="Window/Column" unique_id=225368463]
|
||||
custom_minimum_size = Vector2(0, 1)
|
||||
layout_mode = 2
|
||||
text = "Back"
|
||||
size_flags_horizontal = 8
|
||||
|
||||
[node name="Footer" type="MarginContainer" parent="Window/Column" unique_id=775810662]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 24
|
||||
theme_override_constants/margin_top = 12
|
||||
theme_override_constants/margin_right = 24
|
||||
theme_override_constants/margin_bottom = 12
|
||||
|
||||
[node name="Row" type="HBoxContainer" parent="Window/Column/Footer" unique_id=1592632905]
|
||||
custom_minimum_size = Vector2(0, 38)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 12
|
||||
|
||||
[node name="Hint" type="Label" parent="Window/Column/Footer/Row" unique_id=1935428591]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 4
|
||||
@@ -2,88 +2,86 @@
|
||||
|
||||
[ext_resource type="Script" path="src/SaveSlotItem.cs" id="SaveSlotItemScript"]
|
||||
|
||||
[node name="SaveSlotItem" type="Control" unique_id=2135443384]
|
||||
custom_minimum_size = Vector2(120, 120)
|
||||
[node name="SaveSlotItem" type="Control" unique_id=1515573353]
|
||||
custom_minimum_size = Vector2(160, 140)
|
||||
layout_mode = 3
|
||||
anchors_preset = 0
|
||||
script = ExtResource("SaveSlotItemScript")
|
||||
|
||||
[node name="Background" type="ColorRect" parent="." unique_id=255218239]
|
||||
[node name="Card" type="PanelContainer" parent="." unique_id=368114590]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
color = Color(0.12, 0.12, 0.12, 0.9)
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="ScreenshotRect" type="TextureRect" parent="." unique_id=1290008068]
|
||||
layout_mode = 1
|
||||
anchors_preset = -1
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 0.55
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
[node name="Column" type="VBoxContainer" parent="Card" unique_id=1588140578]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
theme_override_constants/separation = 0
|
||||
|
||||
[node name="Shot" type="PanelContainer" parent="Card/Column" unique_id=1733920571]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="ScreenshotRect" type="TextureRect" parent="Card/Column/Shot" unique_id=1172880873]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
expand_mode = 1
|
||||
stretch_mode = 5
|
||||
stretch_mode = 6
|
||||
|
||||
[node name="DateLabel" type="Label" parent="." unique_id=408317888]
|
||||
layout_mode = 1
|
||||
anchors_preset = -1
|
||||
anchor_left = 0.02
|
||||
anchor_top = 0.57
|
||||
anchor_right = 0.98
|
||||
anchor_bottom = 0.68
|
||||
grow_horizontal = 2
|
||||
theme_override_font_sizes/font_size = 12
|
||||
[node name="Info" type="MarginContainer" parent="Card/Column" unique_id=288868314]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
theme_override_constants/margin_left = 12
|
||||
theme_override_constants/margin_top = 8
|
||||
theme_override_constants/margin_right = 12
|
||||
theme_override_constants/margin_bottom = 10
|
||||
|
||||
[node name="SceneLabel" type="Label" parent="." unique_id=1968956898]
|
||||
layout_mode = 1
|
||||
anchors_preset = -1
|
||||
anchor_left = 0.02
|
||||
anchor_top = 0.70
|
||||
anchor_right = 0.98
|
||||
anchor_bottom = 0.80
|
||||
grow_horizontal = 2
|
||||
theme_override_font_sizes/font_size = 11
|
||||
[node name="Column" type="VBoxContainer" parent="Card/Column/Info" unique_id=700616276]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
theme_override_constants/separation = 2
|
||||
|
||||
[node name="TimeLabel" type="Label" parent="." unique_id=698436659]
|
||||
layout_mode = 1
|
||||
anchors_preset = -1
|
||||
anchor_left = 0.02
|
||||
anchor_top = 0.83
|
||||
anchor_right = 0.56
|
||||
anchor_bottom = 0.95
|
||||
grow_horizontal = 2
|
||||
theme_override_font_sizes/font_size = 10
|
||||
[node name="DateLabel" type="Label" parent="Card/Column/Info/Column" unique_id=931970274]
|
||||
layout_mode = 2
|
||||
clip_text = true
|
||||
|
||||
[node name="StatsLabel" type="Label" parent="." unique_id=400000001]
|
||||
layout_mode = 1
|
||||
anchors_preset = -1
|
||||
anchor_left = 0.58
|
||||
anchor_top = 0.83
|
||||
anchor_right = 0.98
|
||||
anchor_bottom = 0.95
|
||||
grow_horizontal = 2
|
||||
theme_override_font_sizes/font_size = 10
|
||||
[node name="SceneLabel" type="Label" parent="Card/Column/Info/Column" unique_id=1597878703]
|
||||
layout_mode = 2
|
||||
clip_text = true
|
||||
|
||||
[node name="Meta" type="HBoxContainer" parent="Card/Column/Info/Column" unique_id=1948773627]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="TimeLabel" type="Label" parent="Card/Column/Info/Column/Meta" unique_id=729436693]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="StatsLabel" type="Label" parent="Card/Column/Info/Column/Meta" unique_id=381078225]
|
||||
layout_mode = 2
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="EmptyLabel" type="Label" parent="." unique_id=1262712091]
|
||||
[node name="EmptyLabel" type="Label" parent="." unique_id=1904510000]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_font_sizes/font_size = 14
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="SlotButton" type="Button" parent="." unique_id=1877010914]
|
||||
[node name="SlotButton" type="Button" parent="." unique_id=1369022409]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
focus_mode = 0
|
||||
flat = true
|
||||
+273
-240
@@ -2,369 +2,402 @@
|
||||
|
||||
[ext_resource type="Script" path="src/SettingsMenu.cs" id="SettingsMenuScript"]
|
||||
|
||||
[node name="SettingsMenu" type="Control" unique_id=894644832]
|
||||
[node name="SettingsMenu" type="Control" unique_id=1272946981]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 1
|
||||
script = ExtResource("SettingsMenuScript")
|
||||
|
||||
[node name="Bg" type="ColorRect" parent="." unique_id=21945756]
|
||||
[node name="Backdrop" type="ColorRect" parent="." unique_id=926490946]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
color = Color(0, 0, 0, 1)
|
||||
color = Color(0, 0, 0, 0.9)
|
||||
|
||||
[node name="Panel" type="Panel" parent="." unique_id=1554260654]
|
||||
[node name="Window" type="PanelContainer" parent="." unique_id=1346264907]
|
||||
layout_mode = 1
|
||||
anchors_preset = -1
|
||||
anchor_left = 0.1
|
||||
anchor_top = 0.1
|
||||
anchor_right = 0.9
|
||||
anchor_bottom = 0.9
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -420.0
|
||||
offset_top = -310.0
|
||||
offset_right = 420.0
|
||||
offset_bottom = 310.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Margin" type="MarginContainer" parent="Panel" unique_id=1929120286]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/margin_left = 28
|
||||
theme_override_constants/margin_top = 20
|
||||
theme_override_constants/margin_right = 28
|
||||
theme_override_constants/margin_bottom = 20
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="Panel/Margin" unique_id=150548585]
|
||||
[node name="Column" type="VBoxContainer" parent="Window" unique_id=1929192805]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 0
|
||||
|
||||
[node name="Header" type="MarginContainer" parent="Window/Column" unique_id=123233874]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 24
|
||||
theme_override_constants/margin_top = 16
|
||||
theme_override_constants/margin_right = 16
|
||||
theme_override_constants/margin_bottom = 16
|
||||
|
||||
[node name="Row" type="HBoxContainer" parent="Window/Column/Header" unique_id=918557862]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 24
|
||||
|
||||
[node name="Title" type="Label" parent="Window/Column/Header/Row" unique_id=968405469]
|
||||
layout_mode = 2
|
||||
text = "Settings"
|
||||
uppercase = true
|
||||
|
||||
[node name="Tabs" type="HBoxContainer" parent="Window/Column/Header/Row" unique_id=774582017]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 4
|
||||
|
||||
[node name="GeneralTab" type="Button" parent="Window/Column/Header/Row/Tabs" unique_id=1470465706]
|
||||
layout_mode = 2
|
||||
focus_mode = 0
|
||||
toggle_mode = true
|
||||
|
||||
[node name="AudioTab" type="Button" parent="Window/Column/Header/Row/Tabs" unique_id=1481059729]
|
||||
layout_mode = 2
|
||||
focus_mode = 0
|
||||
toggle_mode = true
|
||||
|
||||
[node name="DisplayTab" type="Button" parent="Window/Column/Header/Row/Tabs" unique_id=600818871]
|
||||
layout_mode = 2
|
||||
focus_mode = 0
|
||||
toggle_mode = true
|
||||
|
||||
[node name="ModsTab" type="Button" parent="Window/Column/Header/Row/Tabs" unique_id=1134775141]
|
||||
layout_mode = 2
|
||||
focus_mode = 0
|
||||
toggle_mode = true
|
||||
|
||||
[node name="Spacer" type="Control" parent="Window/Column/Header/Row" unique_id=956914814]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="CloseButton" type="Button" parent="Window/Column/Header/Row" unique_id=971181459]
|
||||
layout_mode = 2
|
||||
custom_minimum_size = Vector2(38, 38)
|
||||
focus_mode = 0
|
||||
text = "✕"
|
||||
|
||||
[node name="RuleHeader" type="ColorRect" parent="Window/Column" unique_id=100276952]
|
||||
custom_minimum_size = Vector2(0, 1)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Body" type="MarginContainer" parent="Window/Column" unique_id=571795343]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/margin_left = 32
|
||||
theme_override_constants/margin_top = 24
|
||||
theme_override_constants/margin_right = 32
|
||||
theme_override_constants/margin_bottom = 24
|
||||
|
||||
[node name="Pages" type="Control" parent="Window/Column/Body" unique_id=138945425]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="General" type="VBoxContainer" parent="Window/Column/Body/Pages" unique_id=215740431]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/separation = 14
|
||||
|
||||
[node name="Title" type="Label" parent="Panel/Margin/VBox" unique_id=86160906]
|
||||
[node name="LangSection" type="Label" parent="Window/Column/Body/Pages/General" unique_id=1563036971]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 26
|
||||
text = "Settings"
|
||||
horizontal_alignment = 1
|
||||
text = "Language"
|
||||
uppercase = true
|
||||
|
||||
[node name="Tabs" type="TabContainer" parent="Panel/Margin/VBox" unique_id=1524671125]
|
||||
[node name="LangRow" type="HBoxContainer" parent="Window/Column/Body/Pages/General" unique_id=1441486578]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
tab_alignment = 1
|
||||
current_tab = 0
|
||||
theme_override_constants/separation = 24
|
||||
|
||||
[node name="GeneralMargin" type="MarginContainer" parent="Panel/Margin/VBox/Tabs"]
|
||||
[node name="LangLabel" type="Label" parent="Window/Column/Body/Pages/General/LangRow" unique_id=1359724527]
|
||||
custom_minimum_size = Vector2(280, 0)
|
||||
layout_mode = 2
|
||||
metadata/_tab_index = 0
|
||||
theme_override_constants/margin_left = 16
|
||||
theme_override_constants/margin_top = 12
|
||||
theme_override_constants/margin_right = 16
|
||||
theme_override_constants/margin_bottom = 12
|
||||
|
||||
[node name="General" type="VBoxContainer" parent="Panel/Margin/VBox/Tabs/GeneralMargin" unique_id=2028800899]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 12
|
||||
|
||||
[node name="Spacer" type="Control" parent="Panel/Margin/VBox/Tabs/GeneralMargin/General" unique_id=1203872148]
|
||||
custom_minimum_size = Vector2(0, 12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="LangBox" type="HBoxContainer" parent="Panel/Margin/VBox/Tabs/GeneralMargin/General" unique_id=684741721]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 16
|
||||
|
||||
[node name="LangLabel" type="Label" parent="Panel/Margin/VBox/Tabs/GeneralMargin/General/LangBox" unique_id=1707108944]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Language"
|
||||
|
||||
[node name="LangSelect" type="OptionButton" parent="Panel/Margin/VBox/Tabs/GeneralMargin/General/LangBox" unique_id=2037857401]
|
||||
[node name="LangSelect" type="OptionButton" parent="Window/Column/Body/Pages/General/LangRow" unique_id=1451274578]
|
||||
custom_minimum_size = Vector2(400, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 4
|
||||
|
||||
[node name="AudioMargin" type="MarginContainer" parent="Panel/Margin/VBox/Tabs"]
|
||||
layout_mode = 2
|
||||
visible = false
|
||||
metadata/_tab_index = 1
|
||||
theme_override_constants/margin_left = 16
|
||||
theme_override_constants/margin_top = 12
|
||||
theme_override_constants/margin_right = 16
|
||||
theme_override_constants/margin_bottom = 12
|
||||
[node name="Audio" type="VBoxContainer" parent="Window/Column/Body/Pages" unique_id=232944441]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/separation = 14
|
||||
|
||||
[node name="Audio" type="VBoxContainer" parent="Panel/Margin/VBox/Tabs/AudioMargin" unique_id=564523157]
|
||||
[node name="VolumeSection" type="Label" parent="Window/Column/Body/Pages/Audio" unique_id=358076534]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 4
|
||||
text = "Volume"
|
||||
uppercase = true
|
||||
|
||||
[node name="SoundsBox" type="VBoxContainer" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio" unique_id=508426867]
|
||||
[node name="SoundsRow" type="HBoxContainer" parent="Window/Column/Body/Pages/Audio" unique_id=1091551106]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 4
|
||||
theme_override_constants/separation = 24
|
||||
|
||||
[node name="ToggleRow" type="HBoxContainer" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio/SoundsBox" unique_id=274422939]
|
||||
[node name="SoundsLabel" type="Label" parent="Window/Column/Body/Pages/Audio/SoundsRow" unique_id=977377427]
|
||||
custom_minimum_size = Vector2(280, 0)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 12
|
||||
|
||||
[node name="SoundsLabel" type="Label" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio/SoundsBox/ToggleRow" unique_id=1483011032]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Sounds"
|
||||
|
||||
[node name="SoundsToggle" type="CheckButton" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio/SoundsBox/ToggleRow" unique_id=126270437]
|
||||
[node name="SoundsToggle" type="CheckButton" parent="Window/Column/Body/Pages/Audio/SoundsRow" unique_id=294349187]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
focus_mode = 0
|
||||
|
||||
[node name="VolumeRow" type="HBoxContainer" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio/SoundsBox" unique_id=585263148]
|
||||
[node name="SoundsVolume" type="HSlider" parent="Window/Column/Body/Pages/Audio/SoundsRow" unique_id=334275673]
|
||||
custom_minimum_size = Vector2(300, 0)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 12
|
||||
size_flags_vertical = 4
|
||||
max_value = 100.0
|
||||
step = 1.0
|
||||
|
||||
[node name="SoundsVolumeLabel" type="Label" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio/SoundsBox/VolumeRow" unique_id=1327120426]
|
||||
[node name="SoundsValue" type="Label" parent="Window/Column/Body/Pages/Audio/SoundsRow" unique_id=761672957]
|
||||
custom_minimum_size = Vector2(56, 0)
|
||||
layout_mode = 2
|
||||
text = "Volume"
|
||||
|
||||
[node name="SoundsVolume" type="HSlider" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio/SoundsBox/VolumeRow" unique_id=2061615116]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
value = 100.0
|
||||
|
||||
[node name="SoundsVolumeValue" type="Label" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio/SoundsBox/VolumeRow" unique_id=1079108664]
|
||||
custom_minimum_size = Vector2(48, 0)
|
||||
layout_mode = 2
|
||||
text = "100%"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="Sep" type="HSeparator" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio" unique_id=988886928]
|
||||
custom_minimum_size = Vector2(0, 8)
|
||||
[node name="MusicRow" type="HBoxContainer" parent="Window/Column/Body/Pages/Audio" unique_id=1716861232]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 24
|
||||
|
||||
[node name="MusicBox" type="VBoxContainer" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio" unique_id=1316259016]
|
||||
[node name="MusicLabel" type="Label" parent="Window/Column/Body/Pages/Audio/MusicRow" unique_id=1921053802]
|
||||
custom_minimum_size = Vector2(280, 0)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 4
|
||||
|
||||
[node name="ToggleRow2" type="HBoxContainer" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio/MusicBox" unique_id=553618696]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 12
|
||||
|
||||
[node name="MusicLabel" type="Label" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio/MusicBox/ToggleRow2" unique_id=1559600377]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Music"
|
||||
|
||||
[node name="MusicToggle" type="CheckButton" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio/MusicBox/ToggleRow2" unique_id=901979875]
|
||||
[node name="MusicToggle" type="CheckButton" parent="Window/Column/Body/Pages/Audio/MusicRow" unique_id=1546243765]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
focus_mode = 0
|
||||
|
||||
[node name="VolumeRow2" type="HBoxContainer" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio/MusicBox" unique_id=1411284591]
|
||||
[node name="MusicVolume" type="HSlider" parent="Window/Column/Body/Pages/Audio/MusicRow" unique_id=656456481]
|
||||
custom_minimum_size = Vector2(300, 0)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 12
|
||||
size_flags_vertical = 4
|
||||
max_value = 100.0
|
||||
step = 1.0
|
||||
|
||||
[node name="MusicVolumeLabel" type="Label" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio/MusicBox/VolumeRow2" unique_id=1952498749]
|
||||
[node name="MusicValue" type="Label" parent="Window/Column/Body/Pages/Audio/MusicRow" unique_id=1811565676]
|
||||
custom_minimum_size = Vector2(56, 0)
|
||||
layout_mode = 2
|
||||
text = "Volume"
|
||||
|
||||
[node name="MusicVolume" type="HSlider" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio/MusicBox/VolumeRow2" unique_id=504218348]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
value = 5.0
|
||||
|
||||
[node name="MusicVolumeValue" type="Label" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio/MusicBox/VolumeRow2" unique_id=1866810087]
|
||||
custom_minimum_size = Vector2(48, 0)
|
||||
layout_mode = 2
|
||||
text = "5%"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="Sep2" type="HSeparator" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio" unique_id=1486966851]
|
||||
custom_minimum_size = Vector2(0, 8)
|
||||
[node name="AmbientRow" type="HBoxContainer" parent="Window/Column/Body/Pages/Audio" unique_id=483864917]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 24
|
||||
|
||||
[node name="AmbientBox" type="VBoxContainer" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio" unique_id=127969403]
|
||||
[node name="AmbientLabel" type="Label" parent="Window/Column/Body/Pages/Audio/AmbientRow" unique_id=402291449]
|
||||
custom_minimum_size = Vector2(280, 0)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 4
|
||||
|
||||
[node name="ToggleRow3" type="HBoxContainer" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio/AmbientBox" unique_id=338401886]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 12
|
||||
|
||||
[node name="AmbientLabel" type="Label" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio/AmbientBox/ToggleRow3" unique_id=872436692]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Ambient"
|
||||
|
||||
[node name="AmbientToggle" type="CheckButton" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio/AmbientBox/ToggleRow3" unique_id=1160929014]
|
||||
[node name="AmbientToggle" type="CheckButton" parent="Window/Column/Body/Pages/Audio/AmbientRow" unique_id=427650030]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
focus_mode = 0
|
||||
|
||||
[node name="VolumeRow3" type="HBoxContainer" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio/AmbientBox" unique_id=1139865718]
|
||||
[node name="AmbientVolume" type="HSlider" parent="Window/Column/Body/Pages/Audio/AmbientRow" unique_id=354392891]
|
||||
custom_minimum_size = Vector2(300, 0)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 12
|
||||
size_flags_vertical = 4
|
||||
max_value = 100.0
|
||||
step = 1.0
|
||||
|
||||
[node name="AmbientVolumeLabel" type="Label" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio/AmbientBox/VolumeRow3" unique_id=1610793395]
|
||||
[node name="AmbientValue" type="Label" parent="Window/Column/Body/Pages/Audio/AmbientRow" unique_id=1500302873]
|
||||
custom_minimum_size = Vector2(56, 0)
|
||||
layout_mode = 2
|
||||
text = "Volume"
|
||||
|
||||
[node name="AmbientVolume" type="HSlider" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio/AmbientBox/VolumeRow3" unique_id=1343104331]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
value = 5.0
|
||||
|
||||
[node name="AmbientVolumeValue" type="Label" parent="Panel/Margin/VBox/Tabs/AudioMargin/Audio/AmbientBox/VolumeRow3" unique_id=1909728919]
|
||||
custom_minimum_size = Vector2(48, 0)
|
||||
layout_mode = 2
|
||||
text = "5%"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="DisplayMargin" type="MarginContainer" parent="Panel/Margin/VBox/Tabs"]
|
||||
layout_mode = 2
|
||||
visible = false
|
||||
metadata/_tab_index = 2
|
||||
theme_override_constants/margin_left = 16
|
||||
theme_override_constants/margin_top = 12
|
||||
theme_override_constants/margin_right = 16
|
||||
theme_override_constants/margin_bottom = 12
|
||||
[node name="Display" type="VBoxContainer" parent="Window/Column/Body/Pages" unique_id=218390439]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/separation = 14
|
||||
|
||||
[node name="Display" type="VBoxContainer" parent="Panel/Margin/VBox/Tabs/DisplayMargin" unique_id=1371435162]
|
||||
[node name="WindowSection" type="Label" parent="Window/Column/Body/Pages/Display" unique_id=345383153]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
text = "Window"
|
||||
uppercase = true
|
||||
|
||||
[node name="Spacer2" type="Control" parent="Panel/Margin/VBox/Tabs/DisplayMargin/Display" unique_id=270871165]
|
||||
custom_minimum_size = Vector2(0, 12)
|
||||
[node name="ModeRow" type="HBoxContainer" parent="Window/Column/Body/Pages/Display" unique_id=1616575243]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 24
|
||||
|
||||
[node name="ModeBox" type="HBoxContainer" parent="Panel/Margin/VBox/Tabs/DisplayMargin/Display" unique_id=455596205]
|
||||
[node name="ModeLabel" type="Label" parent="Window/Column/Body/Pages/Display/ModeRow" unique_id=207553790]
|
||||
custom_minimum_size = Vector2(280, 0)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 16
|
||||
text = "Window mode"
|
||||
|
||||
[node name="ModeLabel" type="Label" parent="Panel/Margin/VBox/Tabs/DisplayMargin/Display/ModeBox" unique_id=1346202262]
|
||||
[node name="ModeSelect" type="OptionButton" parent="Window/Column/Body/Pages/Display/ModeRow" unique_id=1678024463]
|
||||
custom_minimum_size = Vector2(400, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Window Mode"
|
||||
size_flags_vertical = 4
|
||||
|
||||
[node name="ModeSelect" type="OptionButton" parent="Panel/Margin/VBox/Tabs/DisplayMargin/Display/ModeBox" unique_id=631713497]
|
||||
[node name="MonitorRow" type="HBoxContainer" parent="Window/Column/Body/Pages/Display" unique_id=643742076]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_constants/separation = 24
|
||||
|
||||
[node name="ResBox" type="HBoxContainer" parent="Panel/Margin/VBox/Tabs/DisplayMargin/Display" unique_id=603217917]
|
||||
[node name="MonitorLabel" type="Label" parent="Window/Column/Body/Pages/Display/MonitorRow" unique_id=465414492]
|
||||
custom_minimum_size = Vector2(280, 0)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 16
|
||||
|
||||
[node name="ResLabel" type="Label" parent="Panel/Margin/VBox/Tabs/DisplayMargin/Display/ResBox" unique_id=1714673968]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Resolution"
|
||||
|
||||
[node name="ResSelect" type="OptionButton" parent="Panel/Margin/VBox/Tabs/DisplayMargin/Display/ResBox" unique_id=651322592]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="MonitorBox" type="HBoxContainer" parent="Panel/Margin/VBox/Tabs/DisplayMargin/Display" unique_id=284981749]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 16
|
||||
|
||||
[node name="MonitorLabel" type="Label" parent="Panel/Margin/VBox/Tabs/DisplayMargin/Display/MonitorBox" unique_id=1905911340]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Monitor"
|
||||
|
||||
[node name="MonitorSelect" type="OptionButton" parent="Panel/Margin/VBox/Tabs/DisplayMargin/Display/MonitorBox" unique_id=1334336966]
|
||||
[node name="MonitorSelect" type="OptionButton" parent="Window/Column/Body/Pages/Display/MonitorRow" unique_id=868484966]
|
||||
custom_minimum_size = Vector2(400, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 4
|
||||
|
||||
[node name="ScaleBox" type="HBoxContainer" parent="Panel/Margin/VBox/Tabs/DisplayMargin/Display" unique_id=930383631]
|
||||
[node name="ResRow" type="HBoxContainer" parent="Window/Column/Body/Pages/Display" unique_id=1701841140]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 16
|
||||
theme_override_constants/separation = 24
|
||||
|
||||
[node name="ScaleLabel" type="Label" parent="Panel/Margin/VBox/Tabs/DisplayMargin/Display/ScaleBox" unique_id=1924008447]
|
||||
[node name="ResLabel" type="Label" parent="Window/Column/Body/Pages/Display/ResRow" unique_id=1649779675]
|
||||
custom_minimum_size = Vector2(280, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Scale"
|
||||
text = "Window size"
|
||||
|
||||
[node name="ScaleSelect" type="OptionButton" parent="Panel/Margin/VBox/Tabs/DisplayMargin/Display/ScaleBox" unique_id=2096132155]
|
||||
[node name="ResSelect" type="OptionButton" parent="Window/Column/Body/Pages/Display/ResRow" unique_id=127437834]
|
||||
custom_minimum_size = Vector2(400, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 4
|
||||
|
||||
[node name="VSyncRow" type="HBoxContainer" parent="Panel/Margin/VBox/Tabs/DisplayMargin/Display" unique_id=348516587]
|
||||
[node name="ResHint" type="Label" parent="Window/Column/Body/Pages/Display" unique_id=1953536508]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 12
|
||||
text = "Fullscreen modes use the monitor resolution"
|
||||
|
||||
[node name="VSyncLabel" type="Label" parent="Panel/Margin/VBox/Tabs/DisplayMargin/Display/VSyncRow" unique_id=1204827790]
|
||||
[node name="Gap" type="Control" parent="Window/Column/Body/Pages/Display" unique_id=1362538809]
|
||||
custom_minimum_size = Vector2(0, 10)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "VSync"
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="VSyncToggle" type="CheckButton" parent="Panel/Margin/VBox/Tabs/DisplayMargin/Display/VSyncRow" unique_id=1909935663]
|
||||
[node name="RenderSection" type="Label" parent="Window/Column/Body/Pages/Display" unique_id=1299172762]
|
||||
layout_mode = 2
|
||||
text = "Performance"
|
||||
uppercase = true
|
||||
|
||||
[node name="ModSettingsMargin" type="MarginContainer" parent="Panel/Margin/VBox/Tabs"]
|
||||
[node name="VSyncRow" type="HBoxContainer" parent="Window/Column/Body/Pages/Display" unique_id=886210113]
|
||||
layout_mode = 2
|
||||
visible = false
|
||||
metadata/_tab_index = 3
|
||||
theme_override_constants/margin_left = 16
|
||||
theme_override_constants/margin_top = 12
|
||||
theme_override_constants/margin_right = 16
|
||||
theme_override_constants/margin_bottom = 12
|
||||
theme_override_constants/separation = 24
|
||||
|
||||
[node name="ModSettings" type="HBoxContainer" parent="Panel/Margin/VBox/Tabs/ModSettingsMargin" unique_id=2038671463]
|
||||
[node name="VSyncLabel" type="Label" parent="Window/Column/Body/Pages/Display/VSyncRow" unique_id=700706593]
|
||||
custom_minimum_size = Vector2(280, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/separation = 12
|
||||
text = "V-Sync"
|
||||
|
||||
[node name="LeftPanel" type="VBoxContainer" parent="Panel/Margin/VBox/Tabs/ModSettingsMargin/ModSettings" unique_id=1856089080]
|
||||
[node name="VSyncToggle" type="CheckButton" parent="Window/Column/Body/Pages/Display/VSyncRow" unique_id=1235277494]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
focus_mode = 0
|
||||
|
||||
[node name="FpsRow" type="HBoxContainer" parent="Window/Column/Body/Pages/Display" unique_id=327395126]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 24
|
||||
|
||||
[node name="FpsLabel" type="Label" parent="Window/Column/Body/Pages/Display/FpsRow" unique_id=427174428]
|
||||
custom_minimum_size = Vector2(280, 0)
|
||||
layout_mode = 2
|
||||
text = "Frame rate limit"
|
||||
|
||||
[node name="FpsSelect" type="OptionButton" parent="Window/Column/Body/Pages/Display/FpsRow" unique_id=321930729]
|
||||
custom_minimum_size = Vector2(400, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
|
||||
[node name="Mods" type="HBoxContainer" parent="Window/Column/Body/Pages" unique_id=750857449]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/separation = 24
|
||||
|
||||
[node name="ModListPanel" type="PanelContainer" parent="Window/Column/Body/Pages/Mods" unique_id=1830546854]
|
||||
custom_minimum_size = Vector2(240, 0)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 6
|
||||
|
||||
[node name="ModsTitle" type="Label" parent="Panel/Margin/VBox/Tabs/ModSettingsMargin/ModSettings/LeftPanel" unique_id=1639725410]
|
||||
[node name="Column" type="VBoxContainer" parent="Window/Column/Body/Pages/Mods/ModListPanel" unique_id=1090016444]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 15
|
||||
text = "Mods"
|
||||
horizontal_alignment = 1
|
||||
theme_override_constants/separation = 8
|
||||
|
||||
[node name="ModScroll" type="ScrollContainer" parent="Panel/Margin/VBox/Tabs/ModSettingsMargin/ModSettings/LeftPanel" unique_id=1412822434]
|
||||
[node name="ModsTitle" type="Label" parent="Window/Column/Body/Pages/Mods/ModListPanel/Column" unique_id=284516881]
|
||||
layout_mode = 2
|
||||
text = "Mods"
|
||||
uppercase = true
|
||||
|
||||
[node name="ModScroll" type="ScrollContainer" parent="Window/Column/Body/Pages/Mods/ModListPanel/Column" unique_id=1938378648]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
horizontal_scroll_mode = 0
|
||||
|
||||
[node name="ModList" type="VBoxContainer" parent="Panel/Margin/VBox/Tabs/ModSettingsMargin/ModSettings/LeftPanel/ModScroll" unique_id=543902025]
|
||||
[node name="ModList" type="VBoxContainer" parent="Window/Column/Body/Pages/Mods/ModListPanel/Column/ModScroll" unique_id=316495967]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_constants/separation = 4
|
||||
|
||||
[node name="Detail" type="VBoxContainer" parent="Window/Column/Body/Pages/Mods" unique_id=1311174486]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_constants/separation = 12
|
||||
|
||||
[node name="SelectedModName" type="Label" parent="Window/Column/Body/Pages/Mods/Detail" unique_id=562690072]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Sep" type="VBoxContainer" parent="Panel/Margin/VBox/Tabs/ModSettingsMargin/ModSettings" unique_id=950619413]
|
||||
custom_minimum_size = Vector2(10, 0)
|
||||
[node name="SettingsScroll" type="ScrollContainer" parent="Window/Column/Body/Pages/Mods/Detail" unique_id=1319182580]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
horizontal_scroll_mode = 0
|
||||
|
||||
[node name="RightPanel" type="VBoxContainer" parent="Panel/Margin/VBox/Tabs/ModSettingsMargin/ModSettings" unique_id=272738827]
|
||||
[node name="SettingsContainer" type="VBoxContainer" parent="Window/Column/Body/Pages/Mods/Detail/SettingsScroll" unique_id=951372036]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_constants/separation = 8
|
||||
|
||||
[node name="TopBar" type="HBoxContainer" parent="Panel/Margin/VBox/Tabs/ModSettingsMargin/ModSettings/RightPanel" unique_id=678746437]
|
||||
[node name="Placeholder" type="Label" parent="Window/Column/Body/Pages/Mods/Detail/SettingsScroll/SettingsContainer" unique_id=1476346408]
|
||||
layout_mode = 2
|
||||
text = "No mod settings available"
|
||||
|
||||
[node name="RuleFooter" type="ColorRect" parent="Window/Column" unique_id=304272990]
|
||||
custom_minimum_size = Vector2(0, 1)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Footer" type="MarginContainer" parent="Window/Column" unique_id=1834247695]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 24
|
||||
theme_override_constants/margin_top = 12
|
||||
theme_override_constants/margin_right = 24
|
||||
theme_override_constants/margin_bottom = 12
|
||||
|
||||
[node name="Row" type="HBoxContainer" parent="Window/Column/Footer" unique_id=922090408]
|
||||
custom_minimum_size = Vector2(0, 38)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 12
|
||||
|
||||
[node name="SelectedModName" type="Label" parent="Panel/Margin/VBox/Tabs/ModSettingsMargin/ModSettings/RightPanel/TopBar" unique_id=393931159]
|
||||
[node name="Hint" type="Label" parent="Window/Column/Footer/Row" unique_id=1010204388]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_font_sizes/font_size = 16
|
||||
size_flags_vertical = 4
|
||||
|
||||
[node name="ResetBtn" type="Button" parent="Panel/Margin/VBox/Tabs/ModSettingsMargin/ModSettings/RightPanel/TopBar" unique_id=531057889]
|
||||
[node name="ResetButton" type="Button" parent="Window/Column/Footer/Row" unique_id=1841068864]
|
||||
layout_mode = 2
|
||||
text = "Reset"
|
||||
|
||||
[node name="SettingsScroll" type="ScrollContainer" parent="Panel/Margin/VBox/Tabs/ModSettingsMargin/ModSettings/RightPanel" unique_id=674333894]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
horizontal_scroll_mode = 0
|
||||
|
||||
[node name="SettingsContainer" type="VBoxContainer" parent="Panel/Margin/VBox/Tabs/ModSettingsMargin/ModSettings/RightPanel/SettingsScroll" unique_id=1664189823]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 6
|
||||
|
||||
[node name="Placeholder" type="Label" parent="Panel/Margin/VBox/Tabs/ModSettingsMargin/ModSettings/RightPanel/SettingsScroll/SettingsContainer" unique_id=1501445323]
|
||||
layout_mode = 2
|
||||
text = "No mod settings available"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="BackBtn" type="Button" parent="Panel/Margin/VBox" unique_id=249196016]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
text = "Back"
|
||||
focus_mode = 0
|
||||
+70
-62
@@ -17,11 +17,17 @@ namespace Cthangover.BaseMenu
|
||||
/// readiness (what is missing and what can be installed), saves and the
|
||||
/// last play date — with New game / Continue / Saves / Install / Configure.
|
||||
/// Data comes from <see cref="GameCatalog"/> against all installed mods, so
|
||||
/// it works from the front-end where only the menu is loaded.
|
||||
/// it works from the front-end where only the menu is loaded. The window is
|
||||
/// the menu shell (<see cref="MenuWindowShell"/>, scene <c>game_picker.tscn</c>);
|
||||
/// its footer says whether the games' mods are still compiled in the
|
||||
/// background (<see cref="GameSession.IsWarmingUp"/>) or a start is quick.
|
||||
/// </summary>
|
||||
public partial class GamePickerPanel : Control
|
||||
{
|
||||
private const float HintRefreshSeconds = 0.5f;
|
||||
|
||||
private VBoxContainer _list;
|
||||
private Label _hint;
|
||||
private readonly HashSet<string> _sessionInstalled = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>Raised when the player opens a game's saves; the menu shows its save browser.</summary>
|
||||
@@ -31,58 +37,29 @@ namespace Cthangover.BaseMenu
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
SetAnchorsAndOffsetsPreset(LayoutPreset.FullRect);
|
||||
MouseFilter = MouseFilterEnum.Stop;
|
||||
MenuWindowShell.Style(this, Close);
|
||||
GetNode<Label>(MenuWindowShell.Header + "Title").Text = TranslationServer.Translate("menu/play/title");
|
||||
_list = GetNode<VBoxContainer>(MenuWindowShell.Body + "Scroll/List");
|
||||
_hint = GetNode<Label>(MenuWindowShell.Footer + "Hint");
|
||||
|
||||
var backdrop = new ColorRect { Color = new Color(0, 0, 0, 0.65f) };
|
||||
backdrop.SetAnchorsAndOffsetsPreset(LayoutPreset.FullRect);
|
||||
AddChild(backdrop);
|
||||
|
||||
var panel = new PanelContainer();
|
||||
panel.AddThemeStyleboxOverride("panel", UiKit.Frame(UiTokens.RadiusLg, UiTokens.Surface1, UiTokens.Line));
|
||||
panel.AnchorLeft = panel.AnchorRight = panel.AnchorTop = panel.AnchorBottom = 0.5f;
|
||||
panel.OffsetLeft = -460;
|
||||
panel.OffsetRight = 460;
|
||||
panel.OffsetTop = -310;
|
||||
panel.OffsetBottom = 310;
|
||||
panel.GrowHorizontal = GrowDirection.Both;
|
||||
panel.GrowVertical = GrowDirection.Both;
|
||||
AddChild(panel);
|
||||
|
||||
var margin = new MarginContainer();
|
||||
foreach (var side in new[] { "left", "right", "top", "bottom" })
|
||||
margin.AddThemeConstantOverride($"margin_{side}", UiTokens.Space6);
|
||||
panel.AddChild(margin);
|
||||
|
||||
var column = UiKit.Column(UiTokens.Space4);
|
||||
column.MouseFilter = MouseFilterEnum.Pass;
|
||||
margin.AddChild(column);
|
||||
|
||||
column.AddChild(UiKit.Label(TranslationServer.Translate("menu/play/title"), UiTokens.FontH2, UiTokens.InkBright));
|
||||
|
||||
var scroll = new ScrollContainer
|
||||
{
|
||||
SizeFlagsVertical = SizeFlags.ExpandFill,
|
||||
HorizontalScrollMode = ScrollContainer.ScrollMode.Disabled,
|
||||
};
|
||||
column.AddChild(scroll);
|
||||
|
||||
_list = UiKit.Column(UiTokens.Space3);
|
||||
_list.MouseFilter = MouseFilterEnum.Pass;
|
||||
_list.SizeFlagsHorizontal = SizeFlags.ExpandFill;
|
||||
scroll.AddChild(_list);
|
||||
|
||||
var back = UiKit.Button(TranslationServer.Translate("settings/back"));
|
||||
back.SizeFlagsHorizontal = SizeFlags.ShrinkEnd;
|
||||
back.Pressed += Close;
|
||||
column.AddChild(back);
|
||||
// The footer tells whether the games' mods are still compiled in the background.
|
||||
var timer = new Timer { WaitTime = HintRefreshSeconds, Autostart = true };
|
||||
timer.Timeout += RefreshHint;
|
||||
AddChild(timer);
|
||||
|
||||
Visible = false;
|
||||
}
|
||||
|
||||
private void RefreshHint()
|
||||
{
|
||||
if (Visible)
|
||||
_hint.Text = TranslationServer.Translate(GameSession.IsWarmingUp ? "menu/play/hint_warming" : "menu/play/hint_ready");
|
||||
}
|
||||
|
||||
public void Open()
|
||||
{
|
||||
Visible = true;
|
||||
RefreshHint();
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
@@ -115,22 +92,16 @@ namespace Cthangover.BaseMenu
|
||||
var newest = saves.OrderByDescending(s => s.SaveTime).FirstOrDefault();
|
||||
|
||||
var card = new PanelContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
card.AddThemeStyleboxOverride("panel", UiKit.Frame(UiTokens.RadiusMd, UiTokens.Surface2,
|
||||
ActiveGame.Id == collection.Id ? UiTokens.Accent : UiTokens.LineCard));
|
||||
var cardStyle = UiKit.Frame(UiTokens.RadiusMd, UiTokens.Surface2, ActiveGame.Id == collection.Id ? UiTokens.AccentLine : UiTokens.LineCard);
|
||||
cardStyle.ContentMarginLeft = cardStyle.ContentMarginRight = UiTokens.Space5;
|
||||
cardStyle.ContentMarginTop = cardStyle.ContentMarginBottom = UiTokens.Space5;
|
||||
card.AddThemeStyleboxOverride("panel", cardStyle);
|
||||
|
||||
var row = UiKit.Row(UiTokens.Space4);
|
||||
row.MouseFilter = MouseFilterEnum.Pass;
|
||||
card.AddChild(row);
|
||||
|
||||
var cover = GameCatalog.LoadCover(collection);
|
||||
row.AddChild(new TextureRect
|
||||
{
|
||||
Texture = cover,
|
||||
CustomMinimumSize = new Vector2(176, 99),
|
||||
ExpandMode = TextureRect.ExpandModeEnum.IgnoreSize,
|
||||
StretchMode = TextureRect.StretchModeEnum.KeepAspectCovered,
|
||||
SizeFlagsVertical = SizeFlags.ShrinkCenter,
|
||||
});
|
||||
row.AddChild(BuildCover(game, collection));
|
||||
|
||||
var info = UiKit.Column(UiTokens.Space1);
|
||||
info.SizeFlagsHorizontal = SizeFlags.ExpandFill;
|
||||
@@ -145,24 +116,29 @@ namespace Cthangover.BaseMenu
|
||||
info.AddChild(UiKit.Label(saves.Count == 0
|
||||
? TranslationServer.Translate("menu/play/no_saves")
|
||||
: string.Format(TranslationServer.Translate("menu/play/saves"), saves.Count, newest.SaveTime.ToLocalTime().ToString("dd.MM.yy HH:mm")),
|
||||
UiTokens.FontTiny, UiTokens.Muted));
|
||||
UiTokens.FontSmall, UiTokens.Faint));
|
||||
|
||||
var buttons = UiKit.Column(UiTokens.Space2);
|
||||
buttons.MouseFilter = MouseFilterEnum.Pass;
|
||||
buttons.CustomMinimumSize = new Vector2(190, 0);
|
||||
row.AddChild(buttons);
|
||||
|
||||
var start = UiKit.Button(TranslationServer.Translate("menu/play/new_game"), ButtonKind.Primary);
|
||||
// With a save, Continue is the main action of the card; otherwise New game is.
|
||||
if (newest != null)
|
||||
{
|
||||
var resume = UiKit.Button(TranslationServer.Translate("menu/play/continue"), ButtonKind.Primary);
|
||||
resume.Disabled = !game.IsReady;
|
||||
resume.Pressed += () => GameFlow.RequestLoad(this, collection.Id, newest.FileName);
|
||||
buttons.AddChild(resume);
|
||||
}
|
||||
|
||||
var start = UiKit.Button(TranslationServer.Translate("menu/play/new_game"), newest == null ? ButtonKind.Primary : ButtonKind.Secondary);
|
||||
start.Disabled = !game.IsReady;
|
||||
start.Pressed += () => GameFlow.StartGame(this, collection.Id);
|
||||
buttons.AddChild(start);
|
||||
|
||||
if (newest != null)
|
||||
{
|
||||
var resume = UiKit.Button(TranslationServer.Translate("menu/play/continue"));
|
||||
resume.Disabled = !game.IsReady;
|
||||
resume.Pressed += () => GameFlow.RequestLoad(this, collection.Id, newest.FileName);
|
||||
buttons.AddChild(resume);
|
||||
|
||||
var browse = UiKit.Button(TranslationServer.Translate("menu/play/browse_saves"));
|
||||
browse.Pressed += () => SavesRequested?.Invoke(collection.Id);
|
||||
@@ -187,6 +163,38 @@ namespace Cthangover.BaseMenu
|
||||
return card;
|
||||
}
|
||||
|
||||
/// <summary>The collection's cover in a 16:9 frame; a game without one shows the first letter of its name.</summary>
|
||||
private static Control BuildCover(ModCollectionGameSummary game, ModCollection collection)
|
||||
{
|
||||
var frame = new PanelContainer
|
||||
{
|
||||
CustomMinimumSize = new Vector2(208, 117),
|
||||
SizeFlagsVertical = SizeFlags.ShrinkBegin,
|
||||
MouseFilter = MouseFilterEnum.Ignore,
|
||||
};
|
||||
frame.AddThemeStyleboxOverride("panel", UiKit.Frame(UiTokens.RadiusSm, UiTokens.Well, UiTokens.Line));
|
||||
|
||||
var cover = GameCatalog.LoadCover(collection);
|
||||
if (cover != null)
|
||||
{
|
||||
frame.AddChild(new TextureRect
|
||||
{
|
||||
Texture = cover,
|
||||
ExpandMode = TextureRect.ExpandModeEnum.IgnoreSize,
|
||||
StretchMode = TextureRect.StretchModeEnum.KeepAspectCovered,
|
||||
MouseFilter = MouseFilterEnum.Ignore,
|
||||
});
|
||||
return frame;
|
||||
}
|
||||
|
||||
var name = game.Name ?? collection.Id;
|
||||
var letter = UiKit.Label(string.IsNullOrEmpty(name) ? "?" : name.Substring(0, 1).ToUpperInvariant(), UiTokens.FontH1, UiTokens.Faint);
|
||||
letter.HorizontalAlignment = HorizontalAlignment.Center;
|
||||
letter.VerticalAlignment = VerticalAlignment.Center;
|
||||
frame.AddChild(letter);
|
||||
return frame;
|
||||
}
|
||||
|
||||
private static (string Text, Color Color) Status(ModCollectionGameSummary game)
|
||||
{
|
||||
if (game.IsReady)
|
||||
|
||||
+30
-1
@@ -1,6 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using Cthangover.Core.Games;
|
||||
using Cthangover.Core.Scenes;
|
||||
using Cthangover.Core.Settings;
|
||||
using Cthangover.Core.UI;
|
||||
using Cthangover.Core.UI.Menu;
|
||||
using Cthangover.Core.UI.Theming;
|
||||
using Cthangover.Core.UI.Tool;
|
||||
@@ -54,6 +56,9 @@ namespace Cthangover.BaseMenu
|
||||
private void BuildUi()
|
||||
{
|
||||
CthangoverTheme.Apply(this);
|
||||
// Panels of the session (pause menu, inventory, studio...) live on the
|
||||
// persistent UI layer above every scene; none of them belongs on the title screen.
|
||||
UiHost.Instance?.HideAll();
|
||||
|
||||
_settingsMenu = new ModScenePrefab("scenes/menu/settings_menu.tscn").Instantiate<SettingsMenu>();
|
||||
_settingsMenu.Name = "SettingsPanel";
|
||||
@@ -66,7 +71,8 @@ namespace Cthangover.BaseMenu
|
||||
_saveLoadMenu.Visible = false;
|
||||
AddChild(_saveLoadMenu);
|
||||
|
||||
_gamePicker = new GamePickerPanel { Name = "GamePickerPanel" };
|
||||
_gamePicker = new ModScenePrefab("scenes/menu/game_picker.tscn").Instantiate<GamePickerPanel>();
|
||||
_gamePicker.Name = "GamePickerPanel";
|
||||
AddChild(_gamePicker);
|
||||
_gamePicker.SavesRequested += gameId =>
|
||||
{
|
||||
@@ -85,6 +91,29 @@ namespace Cthangover.BaseMenu
|
||||
CheckModCompilationErrors();
|
||||
|
||||
RefreshButtons();
|
||||
StartWarmUp();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compiles the mods of every ready game into the assembly cache in the
|
||||
/// background while the player looks at the menu (<see cref="GameSession.WarmUp"/>),
|
||||
/// the last played game first, so Play / Continue / Load only load
|
||||
/// cached assemblies instead of compiling behind the loading screen.
|
||||
/// </summary>
|
||||
private static void StartWarmUp()
|
||||
{
|
||||
var last = LastSessionStore.Load()?.GameId;
|
||||
var ids = new List<string>();
|
||||
foreach (var game in GameCatalog.Games())
|
||||
{
|
||||
if (!game.IsReady)
|
||||
continue;
|
||||
if (string.Equals(game.Collection.Id, last, System.StringComparison.OrdinalIgnoreCase))
|
||||
ids.Insert(0, game.Collection.Id);
|
||||
else
|
||||
ids.Add(game.Collection.Id);
|
||||
}
|
||||
GameSession.WarmUp(ids);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using Cthangover.Core.UI.Theming;
|
||||
using Cthangover.Mods.Interface.Theming;
|
||||
using Godot;
|
||||
|
||||
namespace Cthangover.BaseMenu
|
||||
{
|
||||
/// <summary>
|
||||
/// The look of the menu windows (settings, save browser, game picker) — the
|
||||
/// style guide's panel shell: a 0.9 backdrop, the window frame, a header
|
||||
/// with the caps title on the left and the ✕ on the right, 1px rules around
|
||||
/// the body and a footer with a faint hint. The node layout lives in each
|
||||
/// window's scene under the same names (<c>Backdrop</c>, <c>Window/Column/…</c>),
|
||||
/// this class only paints it, so the three windows cannot drift apart.
|
||||
/// </summary>
|
||||
public static class MenuWindowShell
|
||||
{
|
||||
/// <summary>Header row: <c>Title</c>, optional controls, <c>CloseButton</c>.</summary>
|
||||
public const string Header = "Window/Column/Header/Row/";
|
||||
|
||||
/// <summary>Body container of the window.</summary>
|
||||
public const string Body = "Window/Column/Body/";
|
||||
|
||||
/// <summary>Footer row: <c>Hint</c> and optional buttons.</summary>
|
||||
public const string Footer = "Window/Column/Footer/Row/";
|
||||
|
||||
/// <summary>
|
||||
/// Paints the shell of <paramref name="root"/> and wires its close button to
|
||||
/// <paramref name="close"/>; the title, the close tooltip and the footer
|
||||
/// hint keep their texts (callers translate them).
|
||||
/// </summary>
|
||||
public static void Style(Control root, Action close)
|
||||
{
|
||||
CthangoverTheme.Apply(root);
|
||||
root.GetNode<ColorRect>("Backdrop").Color = UiTokens.Backdrop;
|
||||
root.GetNode<PanelContainer>("Window").AddThemeStyleboxOverride("panel", UiKit.Frame(UiTokens.RadiusLg, UiTokens.Surface0, UiTokens.Line));
|
||||
root.GetNode<ColorRect>("Window/Column/RuleHeader").Color = UiTokens.Line;
|
||||
root.GetNode<ColorRect>("Window/Column/RuleFooter").Color = UiTokens.Line;
|
||||
|
||||
var title = root.GetNode<Label>(Header + "Title");
|
||||
title.AddThemeFontSizeOverride("font_size", UiTokens.FontTitle);
|
||||
title.AddThemeColorOverride("font_color", UiTokens.InkBright);
|
||||
|
||||
var closeButton = root.GetNode<Button>(Header + "CloseButton");
|
||||
CthangoverTheme.ApplyIcon(closeButton);
|
||||
closeButton.TooltipText = TranslationServer.Translate("settings/back");
|
||||
closeButton.Pressed += close;
|
||||
|
||||
StyleHint(root.GetNode<Label>(Footer + "Hint"));
|
||||
}
|
||||
|
||||
/// <summary>Faint helper text: footer hints and notes under a setting.</summary>
|
||||
public static void StyleHint(Label label)
|
||||
{
|
||||
label.AddThemeFontSizeOverride("font_size", UiTokens.FontBodySmall);
|
||||
label.AddThemeColorOverride("font_color", UiTokens.Faint);
|
||||
}
|
||||
|
||||
/// <summary>Section caption: small muted caps, as section titles everywhere in the kit.</summary>
|
||||
public static void StyleSection(Label label)
|
||||
{
|
||||
label.AddThemeFontSizeOverride("font_size", UiTokens.FontSmall);
|
||||
label.AddThemeColorOverride("font_color", UiTokens.Muted);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-11
@@ -11,8 +11,9 @@ using Godot;
|
||||
namespace Cthangover.BaseMenu
|
||||
{
|
||||
/// <summary>
|
||||
/// Save/load browser with unlimited slots. The grid shows 4×3 slots per
|
||||
/// screen and scrolls past that. Saves belong to games: in a game the
|
||||
/// Save/load browser with unlimited slots, in the menu window shell
|
||||
/// (<see cref="MenuWindowShell"/>; the game selector sits in the header).
|
||||
/// The grid shows 3×2 slots per screen and scrolls past that. Saves belong to games: in a game the
|
||||
/// panel shows that game's slots (save mode appends one empty "new"
|
||||
/// slot); from the front-end the selector at the top switches between
|
||||
/// games with saves and the saves made before games existed. Loading goes
|
||||
@@ -25,7 +26,7 @@ namespace Cthangover.BaseMenu
|
||||
private OptionButton _gameSelect;
|
||||
private SaveSlotList _slotList;
|
||||
private Label _emptyLabel;
|
||||
private Button _backBtn;
|
||||
private Label _hint;
|
||||
|
||||
private bool _isLoadMode;
|
||||
private string _gameId;
|
||||
@@ -39,17 +40,16 @@ namespace Cthangover.BaseMenu
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_titleLabel = GetNode<Label>("Panel/Margin/VBox/Title");
|
||||
_gameSelect = GetNode<OptionButton>("Panel/Margin/VBox/GameSelect");
|
||||
_slotList = GetNode<SaveSlotList>("Panel/Margin/VBox/Scroll/SlotGrid");
|
||||
_emptyLabel = GetNode<Label>("Panel/Margin/VBox/EmptyLabel");
|
||||
_backBtn = GetNode<Button>("Panel/Margin/VBox/BackBtn");
|
||||
CthangoverTheme.ApplyIcon(_backBtn);
|
||||
MenuWindowShell.Style(this, OnBackPressed);
|
||||
_titleLabel = GetNode<Label>(MenuWindowShell.Header + "Title");
|
||||
_gameSelect = GetNode<OptionButton>(MenuWindowShell.Header + "GameSelect");
|
||||
_slotList = GetNode<SaveSlotList>(MenuWindowShell.Body + "Content/Scroll/SlotGrid");
|
||||
_emptyLabel = GetNode<Label>(MenuWindowShell.Body + "Content/EmptyLabel");
|
||||
_emptyLabel.AddThemeColorOverride("font_color", UiTokens.Faint);
|
||||
_hint = GetNode<Label>(MenuWindowShell.Footer + "Hint");
|
||||
|
||||
_slotList.SlotPressed += OnSlotPressed;
|
||||
_gameSelect.ItemSelected += OnGameSelected;
|
||||
_backBtn.Pressed += OnBackPressed;
|
||||
_backBtn.Text = TranslationServer.Translate("settings/back");
|
||||
Visible = false;
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ namespace Cthangover.BaseMenu
|
||||
{
|
||||
_isLoadMode = load;
|
||||
_titleLabel.Text = TranslationServer.Translate(load ? "ui/load/title" : "ui/save/title");
|
||||
_hint.Text = TranslationServer.Translate(load ? "menu/load/hint" : "menu/save/hint");
|
||||
BuildGameSelect(gameId);
|
||||
Visible = true;
|
||||
CallDeferred(nameof(RefreshSlots));
|
||||
|
||||
+40
-20
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using Cthangover.Core.Settings;
|
||||
using Cthangover.Core.UI.Theming;
|
||||
using Cthangover.Mods.Interface.Base.Lists;
|
||||
using Cthangover.Mods.Interface.Theming;
|
||||
using Godot;
|
||||
|
||||
namespace Cthangover.BaseMenu
|
||||
@@ -8,7 +10,7 @@ namespace Cthangover.BaseMenu
|
||||
/// <summary>
|
||||
/// A single save slot card in the save/load grid. Extends ListItem with
|
||||
/// SaveSlotInfo as the model. Renders differently for empty vs. occupied
|
||||
/// slots: empty slots show a placeholder label; occupied slots display the
|
||||
/// slots: an empty slot is a dark well with "new save"; occupied slots display the
|
||||
/// screenshot, in-game date (from TimeData), real-world save timestamp, and
|
||||
/// scene name. The stats label shows party size. Button click emits SlotPressed
|
||||
/// with the file name for parent handling. Destruct disconnects the button
|
||||
@@ -16,6 +18,7 @@ namespace Cthangover.BaseMenu
|
||||
/// </summary>
|
||||
public partial class SaveSlotItem : ListItem<SaveSlotInfo>
|
||||
{
|
||||
private PanelContainer card;
|
||||
private TextureRect screenshotRect;
|
||||
private Label dateLabel;
|
||||
private Label timeLabel;
|
||||
@@ -33,13 +36,34 @@ namespace Cthangover.BaseMenu
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
screenshotRect = GetNode<TextureRect>("ScreenshotRect");
|
||||
dateLabel = GetNode<Label>("DateLabel");
|
||||
timeLabel = GetNode<Label>("TimeLabel");
|
||||
sceneLabel = GetNode<Label>("SceneLabel");
|
||||
card = GetNode<PanelContainer>("Card");
|
||||
screenshotRect = GetNode<TextureRect>("Card/Column/Shot/ScreenshotRect");
|
||||
GetNode<PanelContainer>("Card/Column/Shot").AddThemeStyleboxOverride("panel", UiKit.Frame(UiTokens.RadiusSm, UiTokens.Well));
|
||||
dateLabel = GetNode<Label>("Card/Column/Info/Column/DateLabel");
|
||||
sceneLabel = GetNode<Label>("Card/Column/Info/Column/SceneLabel");
|
||||
timeLabel = GetNode<Label>("Card/Column/Info/Column/Meta/TimeLabel");
|
||||
statsLabel = GetNode<Label>("Card/Column/Info/Column/Meta/StatsLabel");
|
||||
emptyLabel = GetNode<Label>("EmptyLabel");
|
||||
slotButton = GetNode<Button>("SlotButton");
|
||||
statsLabel = GetNodeOrNull<Label>("StatsLabel");
|
||||
|
||||
Style(dateLabel, UiTokens.FontBodyLarge, UiTokens.InkBright);
|
||||
Style(sceneLabel, UiTokens.FontBodySmall, UiTokens.Muted);
|
||||
Style(timeLabel, UiTokens.FontSmall, UiTokens.Faint);
|
||||
Style(statsLabel, UiTokens.FontSmall, UiTokens.Muted);
|
||||
Style(emptyLabel, UiTokens.FontBody, UiTokens.Faint);
|
||||
|
||||
// The whole card is one flat button: hover and press only draw the gold frame.
|
||||
var hover = UiKit.Frame(UiTokens.RadiusMd, new Color(0, 0, 0, 0), UiTokens.AccentLine);
|
||||
slotButton.AddThemeStyleboxOverride("normal", new StyleBoxEmpty());
|
||||
slotButton.AddThemeStyleboxOverride("hover", hover);
|
||||
slotButton.AddThemeStyleboxOverride("pressed", UiKit.Frame(UiTokens.RadiusMd, UiTokens.AccentSoft, UiTokens.AccentLineStrong));
|
||||
slotButton.AddThemeStyleboxOverride("focus", new StyleBoxEmpty());
|
||||
}
|
||||
|
||||
private static void Style(Label label, int size, Color color)
|
||||
{
|
||||
label.AddThemeFontSizeOverride("font_size", size);
|
||||
label.AddThemeColorOverride("font_color", color);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -52,24 +76,20 @@ namespace Cthangover.BaseMenu
|
||||
{
|
||||
base.Construct(model);
|
||||
|
||||
card.AddThemeStyleboxOverride("panel", model.IsEmpty
|
||||
? UiKit.Frame(UiTokens.RadiusMd, UiTokens.Well, UiTokens.Line)
|
||||
: UiKit.Frame(UiTokens.RadiusMd, UiTokens.Surface2, UiTokens.LineCard));
|
||||
|
||||
if (model.IsEmpty)
|
||||
{
|
||||
screenshotRect.Visible = false;
|
||||
dateLabel.Visible = false;
|
||||
timeLabel.Visible = false;
|
||||
sceneLabel.Visible = false;
|
||||
card.GetNode<Control>("Column").Visible = false;
|
||||
emptyLabel.Visible = true;
|
||||
if (statsLabel != null) statsLabel.Visible = false;
|
||||
emptyLabel.Text = TranslationServer.Translate("ui/save/empty");
|
||||
emptyLabel.Text = TranslationServer.Translate("menu/save/new_slot");
|
||||
}
|
||||
else
|
||||
{
|
||||
emptyLabel.Visible = false;
|
||||
screenshotRect.Visible = true;
|
||||
dateLabel.Visible = true;
|
||||
timeLabel.Visible = true;
|
||||
sceneLabel.Visible = true;
|
||||
if (statsLabel != null) statsLabel.Visible = true;
|
||||
card.GetNode<Control>("Column").Visible = true;
|
||||
|
||||
var tex = SaveScreenshotService.LoadScreenshotFromPath(model.ScreenshotPath);
|
||||
if (tex != null)
|
||||
@@ -85,10 +105,10 @@ namespace Cthangover.BaseMenu
|
||||
: "";
|
||||
|
||||
sceneLabel.Text = model.SceneName;
|
||||
sceneLabel.Visible = !string.IsNullOrEmpty(model.SceneName) && model.SceneName != "unknown";
|
||||
|
||||
if (statsLabel != null)
|
||||
statsLabel.Text = TranslationServer.Translate("ui/save/party_size").ToString()
|
||||
.Replace("{count}", model.CharacterCount.ToString());
|
||||
statsLabel.Text = TranslationServer.Translate("ui/save/party_size").ToString()
|
||||
.Replace("{count}", model.CharacterCount.ToString());
|
||||
}
|
||||
|
||||
slotButton.Pressed += OnPressed;
|
||||
|
||||
+269
-182
@@ -9,6 +9,7 @@ using Cthangover.Core.Utils;
|
||||
using Godot;
|
||||
|
||||
using Cthangover.Mods.Interface.Menu;
|
||||
using Cthangover.Mods.Interface.Theming;
|
||||
namespace Cthangover.BaseMenu
|
||||
{
|
||||
/// <summary>
|
||||
@@ -19,8 +20,12 @@ namespace Cthangover.BaseMenu
|
||||
/// immediate effect, then emit LanguageChanged so parent menus can refresh
|
||||
/// their translated button text. Sliders become non-editable when their
|
||||
/// toggle is off, enforcing the "disabled means off" semantics visually.
|
||||
/// Display changes (window mode, resolution, monitor, V-Sync, scale) are
|
||||
/// applied live via DisplayServer and ProjectSettings.
|
||||
/// Display changes (window mode, monitor, window size, V-Sync, frame rate
|
||||
/// cap) are applied live through <see cref="DisplaySettingsService"/> and saved
|
||||
/// at once, so the next start restores exactly what is on screen; the lists
|
||||
/// come from <see cref="DisplayOptions"/>, shared with the first-run launcher.
|
||||
/// The window size list is rebuilt for the chosen monitor and is disabled in
|
||||
/// the fullscreen modes, which always run at the monitor's resolution.
|
||||
/// </summary>
|
||||
public partial class SettingsMenu : Control, ISettingsPanel
|
||||
{
|
||||
@@ -30,7 +35,20 @@ namespace Cthangover.BaseMenu
|
||||
/// refresh their translated button text.
|
||||
/// </summary>
|
||||
public event Action LanguageChanged;
|
||||
private TabContainer _tabs;
|
||||
private const string Header = MenuWindowShell.Header;
|
||||
private const string Pages = "Window/Column/Body/Pages/";
|
||||
private const string Audio = Pages + "Audio/";
|
||||
private const string Display = Pages + "Display/";
|
||||
private const string Mods = Pages + "Mods/";
|
||||
|
||||
/// <summary>Names of <see cref="LanguageCode.Supported"/>, same order.</summary>
|
||||
private static readonly string[] LanguageKeys = { "settings/lang_ru", "settings/lang_en", "settings/lang_zh" };
|
||||
|
||||
private static readonly string[] TabKeys = { "settings/tab_general", "settings/tab_audio", "settings/tab_display", "settings/tab_mods" };
|
||||
|
||||
private Button[] _tabButtons;
|
||||
private Control[] _pages;
|
||||
private int _tab;
|
||||
private CheckButton _soundsToggle;
|
||||
private CheckButton _musicToggle;
|
||||
private CheckButton _ambientToggle;
|
||||
@@ -41,11 +59,11 @@ namespace Cthangover.BaseMenu
|
||||
private Label _musicVolumeValue;
|
||||
private Label _ambientVolumeValue;
|
||||
private OptionButton _langSelect;
|
||||
private Button _backBtn;
|
||||
private Button _resetButton;
|
||||
private Label _footerHint;
|
||||
|
||||
private VBoxContainer _modListContainer;
|
||||
private Label _selectedModName;
|
||||
private Button _resetModSettingsBtn;
|
||||
private VBoxContainer _settingsContainer;
|
||||
private string _selectedModId;
|
||||
private readonly Dictionary<Control, string> _controlKeys = new();
|
||||
@@ -53,72 +71,92 @@ namespace Cthangover.BaseMenu
|
||||
private OptionButton _windowModeSelect;
|
||||
private OptionButton _resolutionSelect;
|
||||
private OptionButton _monitorSelect;
|
||||
private OptionButton _scaleSelect;
|
||||
private OptionButton _fpsSelect;
|
||||
private CheckButton _vsyncToggle;
|
||||
private Label _resHint;
|
||||
private List<Vector2I> _windowSizes = new();
|
||||
|
||||
private readonly Dictionary<Label, string> _labelKeys = new();
|
||||
private bool _initialized;
|
||||
|
||||
private static readonly Vector2I[] CommonResolutions =
|
||||
{
|
||||
new(1280, 720),
|
||||
new(1366, 768),
|
||||
new(1600, 900),
|
||||
new(1920, 1080),
|
||||
new(2560, 1440),
|
||||
new(3840, 2160),
|
||||
};
|
||||
|
||||
private static readonly int[] ScaleOptions = { 1, 2, 3, 4 };
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_tabs = GetNode<TabContainer>("Panel/Margin/VBox/Tabs");
|
||||
MenuWindowShell.Style(this, OnBackPressed);
|
||||
RegisterLabel(GetNode<Label>(Header + "Title"), "menu/settings");
|
||||
|
||||
_langSelect = GetNode<OptionButton>("Panel/Margin/VBox/Tabs/GeneralMargin/General/LangBox/LangSelect");
|
||||
_tabButtons = new[]
|
||||
{
|
||||
GetNode<Button>(Header + "Tabs/GeneralTab"),
|
||||
GetNode<Button>(Header + "Tabs/AudioTab"),
|
||||
GetNode<Button>(Header + "Tabs/DisplayTab"),
|
||||
GetNode<Button>(Header + "Tabs/ModsTab"),
|
||||
};
|
||||
_pages = new[]
|
||||
{
|
||||
GetNode<Control>(Pages + "General"),
|
||||
GetNode<Control>(Pages + "Audio"),
|
||||
GetNode<Control>(Pages + "Display"),
|
||||
GetNode<Control>(Pages + "Mods"),
|
||||
};
|
||||
var tabGroup = new ButtonGroup();
|
||||
foreach (var tab in _tabButtons)
|
||||
{
|
||||
CthangoverTheme.ApplyTab(tab);
|
||||
tab.ButtonGroup = tabGroup;
|
||||
}
|
||||
tabGroup.Pressed += OnTabPressed;
|
||||
|
||||
_soundsToggle = GetNode<CheckButton>("Panel/Margin/VBox/Tabs/AudioMargin/Audio/SoundsBox/ToggleRow/SoundsToggle");
|
||||
_soundsVolume = GetNode<HSlider>("Panel/Margin/VBox/Tabs/AudioMargin/Audio/SoundsBox/VolumeRow/SoundsVolume");
|
||||
_soundsVolumeValue = GetNode<Label>("Panel/Margin/VBox/Tabs/AudioMargin/Audio/SoundsBox/VolumeRow/SoundsVolumeValue");
|
||||
_langSelect = GetNode<OptionButton>(Pages + "General/LangRow/LangSelect");
|
||||
|
||||
_musicToggle = GetNode<CheckButton>("Panel/Margin/VBox/Tabs/AudioMargin/Audio/MusicBox/ToggleRow2/MusicToggle");
|
||||
_musicVolume = GetNode<HSlider>("Panel/Margin/VBox/Tabs/AudioMargin/Audio/MusicBox/VolumeRow2/MusicVolume");
|
||||
_musicVolumeValue = GetNode<Label>("Panel/Margin/VBox/Tabs/AudioMargin/Audio/MusicBox/VolumeRow2/MusicVolumeValue");
|
||||
_soundsToggle = GetNode<CheckButton>(Audio + "SoundsRow/SoundsToggle");
|
||||
_soundsVolume = GetNode<HSlider>(Audio + "SoundsRow/SoundsVolume");
|
||||
_soundsVolumeValue = GetNode<Label>(Audio + "SoundsRow/SoundsValue");
|
||||
_musicToggle = GetNode<CheckButton>(Audio + "MusicRow/MusicToggle");
|
||||
_musicVolume = GetNode<HSlider>(Audio + "MusicRow/MusicVolume");
|
||||
_musicVolumeValue = GetNode<Label>(Audio + "MusicRow/MusicValue");
|
||||
_ambientToggle = GetNode<CheckButton>(Audio + "AmbientRow/AmbientToggle");
|
||||
_ambientVolume = GetNode<HSlider>(Audio + "AmbientRow/AmbientVolume");
|
||||
_ambientVolumeValue = GetNode<Label>(Audio + "AmbientRow/AmbientValue");
|
||||
foreach (var value in new[] { _soundsVolumeValue, _musicVolumeValue, _ambientVolumeValue })
|
||||
value.AddThemeColorOverride("font_color", UiTokens.AccentBright);
|
||||
|
||||
_ambientToggle = GetNode<CheckButton>("Panel/Margin/VBox/Tabs/AudioMargin/Audio/AmbientBox/ToggleRow3/AmbientToggle");
|
||||
_ambientVolume = GetNode<HSlider>("Panel/Margin/VBox/Tabs/AudioMargin/Audio/AmbientBox/VolumeRow3/AmbientVolume");
|
||||
_ambientVolumeValue = GetNode<Label>("Panel/Margin/VBox/Tabs/AudioMargin/Audio/AmbientBox/VolumeRow3/AmbientVolumeValue");
|
||||
_windowModeSelect = GetNode<OptionButton>(Display + "ModeRow/ModeSelect");
|
||||
_resolutionSelect = GetNode<OptionButton>(Display + "ResRow/ResSelect");
|
||||
_monitorSelect = GetNode<OptionButton>(Display + "MonitorRow/MonitorSelect");
|
||||
_fpsSelect = GetNode<OptionButton>(Display + "FpsRow/FpsSelect");
|
||||
_vsyncToggle = GetNode<CheckButton>(Display + "VSyncRow/VSyncToggle");
|
||||
_resHint = GetNode<Label>(Display + "ResHint");
|
||||
MenuWindowShell.StyleHint(_resHint);
|
||||
|
||||
_windowModeSelect = GetNode<OptionButton>("Panel/Margin/VBox/Tabs/DisplayMargin/Display/ModeBox/ModeSelect");
|
||||
_resolutionSelect = GetNode<OptionButton>("Panel/Margin/VBox/Tabs/DisplayMargin/Display/ResBox/ResSelect");
|
||||
_monitorSelect = GetNode<OptionButton>("Panel/Margin/VBox/Tabs/DisplayMargin/Display/MonitorBox/MonitorSelect");
|
||||
_scaleSelect = GetNode<OptionButton>("Panel/Margin/VBox/Tabs/DisplayMargin/Display/ScaleBox/ScaleSelect");
|
||||
_vsyncToggle = GetNode<CheckButton>("Panel/Margin/VBox/Tabs/DisplayMargin/Display/VSyncRow/VSyncToggle");
|
||||
GetNode<PanelContainer>(Mods + "ModListPanel").AddThemeStyleboxOverride("panel", ListPanelStyle());
|
||||
_modListContainer = GetNode<VBoxContainer>(Mods + "ModListPanel/Column/ModScroll/ModList");
|
||||
_selectedModName = GetNode<Label>(Mods + "Detail/SelectedModName");
|
||||
_selectedModName.AddThemeFontSizeOverride("font_size", UiTokens.FontH3);
|
||||
_selectedModName.AddThemeColorOverride("font_color", UiTokens.InkBright);
|
||||
_settingsContainer = GetNode<VBoxContainer>(Mods + "Detail/SettingsScroll/SettingsContainer");
|
||||
|
||||
_backBtn = GetNode<Button>("Panel/Margin/VBox/BackBtn");
|
||||
CthangoverTheme.ApplyIcon(_backBtn);
|
||||
_resetButton = GetNode<Button>("Window/Column/Footer/Row/ResetButton");
|
||||
CthangoverTheme.ApplyDanger(_resetButton);
|
||||
_resetButton.Pressed += OnResetPressed;
|
||||
_footerHint = GetNode<Label>("Window/Column/Footer/Row/Hint");
|
||||
RegisterLabel(_footerHint, "settings/saved_at_once");
|
||||
|
||||
_modListContainer = GetNode<VBoxContainer>("Panel/Margin/VBox/Tabs/ModSettingsMargin/ModSettings/LeftPanel/ModScroll/ModList");
|
||||
_selectedModName = GetNode<Label>("Panel/Margin/VBox/Tabs/ModSettingsMargin/ModSettings/RightPanel/TopBar/SelectedModName");
|
||||
_resetModSettingsBtn = GetNode<Button>("Panel/Margin/VBox/Tabs/ModSettingsMargin/ModSettings/RightPanel/TopBar/ResetBtn");
|
||||
CthangoverTheme.ApplyDanger(_resetModSettingsBtn);
|
||||
_settingsContainer = GetNode<VBoxContainer>("Panel/Margin/VBox/Tabs/ModSettingsMargin/ModSettings/RightPanel/SettingsScroll/SettingsContainer");
|
||||
|
||||
RegisterLabel(GetNode<Label>("Panel/Margin/VBox/Title"), "menu/settings");
|
||||
RegisterLabel(GetNode<Label>("Panel/Margin/VBox/Tabs/GeneralMargin/General/LangBox/LangLabel"), "settings/language");
|
||||
RegisterLabel(GetNode<Label>("Panel/Margin/VBox/Tabs/AudioMargin/Audio/SoundsBox/ToggleRow/SoundsLabel"), "settings/sounds");
|
||||
RegisterLabel(GetNode<Label>("Panel/Margin/VBox/Tabs/AudioMargin/Audio/SoundsBox/VolumeRow/SoundsVolumeLabel"), "settings/volume");
|
||||
RegisterLabel(GetNode<Label>("Panel/Margin/VBox/Tabs/AudioMargin/Audio/MusicBox/ToggleRow2/MusicLabel"), "settings/music");
|
||||
RegisterLabel(GetNode<Label>("Panel/Margin/VBox/Tabs/AudioMargin/Audio/MusicBox/VolumeRow2/MusicVolumeLabel"), "settings/volume");
|
||||
RegisterLabel(GetNode<Label>("Panel/Margin/VBox/Tabs/AudioMargin/Audio/AmbientBox/ToggleRow3/AmbientLabel"), "settings/ambient");
|
||||
RegisterLabel(GetNode<Label>("Panel/Margin/VBox/Tabs/AudioMargin/Audio/AmbientBox/VolumeRow3/AmbientVolumeLabel"), "settings/volume");
|
||||
RegisterLabel(GetNode<Label>("Panel/Margin/VBox/Tabs/DisplayMargin/Display/ModeBox/ModeLabel"), "settings/window_mode");
|
||||
RegisterLabel(GetNode<Label>("Panel/Margin/VBox/Tabs/DisplayMargin/Display/ResBox/ResLabel"), "settings/resolution");
|
||||
RegisterLabel(GetNode<Label>("Panel/Margin/VBox/Tabs/DisplayMargin/Display/MonitorBox/MonitorLabel"), "settings/monitor");
|
||||
RegisterLabel(GetNode<Label>("Panel/Margin/VBox/Tabs/DisplayMargin/Display/ScaleBox/ScaleLabel"), "settings/scale");
|
||||
RegisterLabel(GetNode<Label>("Panel/Margin/VBox/Tabs/DisplayMargin/Display/VSyncRow/VSyncLabel"), "settings/vsync");
|
||||
RegisterLabel(GetNode<Label>("Panel/Margin/VBox/Tabs/ModSettingsMargin/ModSettings/LeftPanel/ModsTitle"), "settings/mod_list_title");
|
||||
RegisterLabel(GetNode<Label>("Panel/Margin/VBox/Tabs/ModSettingsMargin/ModSettings/RightPanel/SettingsScroll/SettingsContainer/Placeholder"), "settings/mods_no_settings");
|
||||
RegisterSection(Pages + "General/LangSection", "settings/language");
|
||||
RegisterLabel(GetNode<Label>(Pages + "General/LangRow/LangLabel"), "settings/interface_language");
|
||||
RegisterSection(Audio + "VolumeSection", "settings/volume");
|
||||
RegisterLabel(GetNode<Label>(Audio + "SoundsRow/SoundsLabel"), "settings/sounds");
|
||||
RegisterLabel(GetNode<Label>(Audio + "MusicRow/MusicLabel"), "settings/music");
|
||||
RegisterLabel(GetNode<Label>(Audio + "AmbientRow/AmbientLabel"), "settings/ambient");
|
||||
RegisterSection(Display + "WindowSection", "settings/section_window");
|
||||
RegisterLabel(GetNode<Label>(Display + "ModeRow/ModeLabel"), "settings/window_mode");
|
||||
RegisterLabel(GetNode<Label>(Display + "ResRow/ResLabel"), "settings/resolution");
|
||||
RegisterLabel(GetNode<Label>(Display + "MonitorRow/MonitorLabel"), "settings/monitor");
|
||||
RegisterLabel(_resHint, "settings/resolution_fullscreen_hint");
|
||||
RegisterSection(Display + "RenderSection", "settings/section_performance");
|
||||
RegisterLabel(GetNode<Label>(Display + "VSyncRow/VSyncLabel"), "settings/vsync");
|
||||
RegisterLabel(GetNode<Label>(Display + "FpsRow/FpsLabel"), "settings/max_fps");
|
||||
RegisterSection(Mods + "ModListPanel/Column/ModsTitle", "settings/mod_list_title");
|
||||
RegisterLabel(GetNode<Label>(Mods + "Detail/SettingsScroll/SettingsContainer/Placeholder"), "settings/mods_no_settings");
|
||||
|
||||
_soundsToggle.Toggled += OnSoundsToggled;
|
||||
_musicToggle.Toggled += OnMusicToggled;
|
||||
@@ -127,27 +165,73 @@ namespace Cthangover.BaseMenu
|
||||
_musicVolume.ValueChanged += OnMusicVolumeChanged;
|
||||
_ambientVolume.ValueChanged += OnAmbientVolumeChanged;
|
||||
_langSelect.ItemSelected += OnLanguageSelected;
|
||||
_backBtn.Pressed += OnBackPressed;
|
||||
_backBtn.Text = TranslationServer.Translate("settings/back");
|
||||
|
||||
_windowModeSelect.ItemSelected += OnDisplaySettingChanged;
|
||||
_resolutionSelect.ItemSelected += OnDisplaySettingChanged;
|
||||
_monitorSelect.ItemSelected += OnDisplaySettingChanged;
|
||||
_scaleSelect.ItemSelected += OnDisplaySettingChanged;
|
||||
_vsyncToggle.Toggled += OnDisplayToggleChanged;
|
||||
_resetModSettingsBtn.Pressed += OnResetModSettings;
|
||||
_windowModeSelect.ItemSelected += OnWindowSettingChanged;
|
||||
_resolutionSelect.ItemSelected += OnWindowSettingChanged;
|
||||
_monitorSelect.ItemSelected += OnMonitorSelected;
|
||||
_fpsSelect.ItemSelected += OnRenderingSettingChanged;
|
||||
_vsyncToggle.Toggled += OnVsyncToggled;
|
||||
|
||||
LoadSettingsToUI();
|
||||
BuildModList();
|
||||
ShowTab(0);
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
/// <summary>Index of the visible tab: 0 general, 1 sound, 2 display, 3 mod settings.</summary>
|
||||
public int CurrentTab => _tab;
|
||||
|
||||
/// <summary>Shows tab <paramref name="index"/>: its page, the gold tab button and the footer reset that belongs to it.</summary>
|
||||
public void ShowTab(int index)
|
||||
{
|
||||
_tab = Mathf.Clamp(index, 0, _pages.Length - 1);
|
||||
for (var i = 0; i < _pages.Length; i++)
|
||||
{
|
||||
_pages[i].Visible = i == _tab;
|
||||
_tabButtons[i].SetPressedNoSignal(i == _tab);
|
||||
}
|
||||
UpdateResetButton();
|
||||
}
|
||||
|
||||
private void OnTabPressed(BaseButton button) => ShowTab(Array.IndexOf(_tabButtons, button));
|
||||
|
||||
/// <summary>The footer reset acts on the visible tab: display settings, or the selected mod's settings.</summary>
|
||||
private void UpdateResetButton()
|
||||
{
|
||||
_resetButton.Visible = _tab == 2 || (_tab == 3 && !string.IsNullOrEmpty(_selectedModId));
|
||||
_resetButton.Text = TranslationServer.Translate(_tab == 2 ? "settings/display_reset" : "settings/mods_reset");
|
||||
}
|
||||
|
||||
private void OnResetPressed()
|
||||
{
|
||||
if (_tab == 2)
|
||||
OnResetDisplay();
|
||||
else if (_tab == 3)
|
||||
OnResetModSettings();
|
||||
}
|
||||
|
||||
private void RegisterLabel(Label label, string key)
|
||||
{
|
||||
if (label != null)
|
||||
_labelKeys[label] = key;
|
||||
}
|
||||
|
||||
/// <summary>Section caption: small muted caps, as section titles everywhere in the kit.</summary>
|
||||
private void RegisterSection(string path, string key)
|
||||
{
|
||||
var label = GetNode<Label>(path);
|
||||
MenuWindowShell.StyleSection(label);
|
||||
RegisterLabel(label, key);
|
||||
}
|
||||
|
||||
private static StyleBoxFlat ListPanelStyle()
|
||||
{
|
||||
var style = UiKit.Frame(UiTokens.RadiusMd, UiTokens.Surface2, UiTokens.Line);
|
||||
style.ContentMarginLeft = style.ContentMarginRight = UiTokens.Space5;
|
||||
style.ContentMarginTop = style.ContentMarginBottom = UiTokens.Space5;
|
||||
return style;
|
||||
}
|
||||
|
||||
private void RefreshUIText()
|
||||
{
|
||||
foreach (var kv in _labelKeys)
|
||||
@@ -155,12 +239,9 @@ namespace Cthangover.BaseMenu
|
||||
if (IsInstanceValid(kv.Key))
|
||||
kv.Key.Text = TranslationServer.Translate(kv.Value);
|
||||
}
|
||||
_backBtn.Text = TranslationServer.Translate("settings/back");
|
||||
_tabs.SetTabTitle(0, TranslationServer.Translate("settings/tab_general"));
|
||||
_tabs.SetTabTitle(1, TranslationServer.Translate("settings/tab_audio"));
|
||||
_tabs.SetTabTitle(2, TranslationServer.Translate("settings/tab_display"));
|
||||
_tabs.SetTabTitle(3, TranslationServer.Translate("settings/tab_mods"));
|
||||
_resetModSettingsBtn.Text = TranslationServer.Translate("settings/mods_reset");
|
||||
for (var i = 0; i < _tabButtons.Length; i++)
|
||||
_tabButtons[i].Text = TranslationServer.Translate(TabKeys[i]);
|
||||
UpdateResetButton();
|
||||
PopulateLangOptions();
|
||||
PopulateDisplayOptions();
|
||||
}
|
||||
@@ -180,96 +261,86 @@ namespace Cthangover.BaseMenu
|
||||
_musicVolumeValue.Text = $"{s.MusicsVolume}%";
|
||||
_ambientVolumeValue.Text = $"{s.AmbientVolume}%";
|
||||
|
||||
_vsyncToggle.ButtonPressed = s.VsyncEnabled;
|
||||
_windowModeSelect.Selected = WindowModeToIndex(s.WindowMode);
|
||||
|
||||
UpdateSlidersEditable();
|
||||
PopulateLangOptions();
|
||||
PopulateDisplayOptions();
|
||||
RefreshUIText();
|
||||
LoadDisplayToUI(s);
|
||||
}
|
||||
|
||||
SelectCurrentResolution(s);
|
||||
SelectCurrentMonitor(s);
|
||||
SelectCurrentScale(s);
|
||||
/// <summary>Shows the saved display settings; the window size list is built for the saved monitor.</summary>
|
||||
private void LoadDisplayToUI(SettingsData s)
|
||||
{
|
||||
_windowModeSelect.Selected = DisplayOptions.WindowModeIndex(s.WindowMode);
|
||||
_monitorSelect.Selected = DisplaySettingsService.ClampScreen(s.CurrentScreen);
|
||||
FillWindowSizes(new Vector2I(s.ResolutionWidth, s.ResolutionHeight));
|
||||
_vsyncToggle.ButtonPressed = s.VsyncEnabled;
|
||||
_fpsSelect.Selected = DisplayOptions.FrameRateIndex(s.MaxFps);
|
||||
UpdateWindowSizeEditable();
|
||||
}
|
||||
|
||||
private void PopulateLangOptions()
|
||||
{
|
||||
_langSelect.Clear();
|
||||
_langSelect.AddItem(TranslationServer.Translate("settings/lang_ru"));
|
||||
_langSelect.AddItem(TranslationServer.Translate("settings/lang_en"));
|
||||
_langSelect.AddItem(TranslationServer.Translate("settings/lang_zh"));
|
||||
foreach (var key in LanguageKeys)
|
||||
_langSelect.AddItem(TranslationServer.Translate(key));
|
||||
|
||||
var s = GameData.Instance?.Settings;
|
||||
if (s == null) return;
|
||||
_langSelect.Selected = s.Language switch { "en" => 1, "zh" => 2, _ => 0 };
|
||||
_langSelect.Selected = Mathf.Max(0, System.Array.IndexOf(LanguageCode.Supported, LanguageCode.Normalize(s.Language) ?? LanguageCode.Supported[0]));
|
||||
}
|
||||
|
||||
/// <summary>Re-fills the translated display lists, keeping the selected items.</summary>
|
||||
private void PopulateDisplayOptions()
|
||||
{
|
||||
var previousWindow = _windowModeSelect.Selected;
|
||||
var previousRes = _resolutionSelect.Selected;
|
||||
var previousMon = _monitorSelect.Selected;
|
||||
var previousScale = _scaleSelect.Selected;
|
||||
var mode = _windowModeSelect.Selected;
|
||||
var monitor = _monitorSelect.Selected;
|
||||
var fps = _fpsSelect.Selected;
|
||||
|
||||
_windowModeSelect.Clear();
|
||||
_windowModeSelect.AddItem(TranslationServer.Translate("settings/window_mode_windowed"));
|
||||
_windowModeSelect.AddItem(TranslationServer.Translate("settings/window_mode_borderless"));
|
||||
_windowModeSelect.AddItem(TranslationServer.Translate("settings/window_mode_fullscreen"));
|
||||
|
||||
_resolutionSelect.Clear();
|
||||
foreach (var r in CommonResolutions)
|
||||
_resolutionSelect.AddItem($"{r.X}×{r.Y}");
|
||||
foreach (var key in DisplayOptions.WindowModeKeys)
|
||||
_windowModeSelect.AddItem(TranslationServer.Translate(key));
|
||||
|
||||
_monitorSelect.Clear();
|
||||
var count = DisplayServer.GetScreenCount();
|
||||
if (count <= 0) count = 1;
|
||||
for (var i = 0; i < count; i++)
|
||||
for (var i = 0; i < DisplaySettingsService.ScreenCount; i++)
|
||||
{
|
||||
var size = DisplayServer.ScreenGetSize(i);
|
||||
var label = string.Format(TranslationServer.Translate("settings/monitor_screen"), i + 1, size.X, size.Y);
|
||||
_monitorSelect.AddItem(label);
|
||||
var key = i == DisplayServer.GetPrimaryScreen() ? "settings/monitor_screen_primary" : "settings/monitor_screen";
|
||||
_monitorSelect.AddItem(string.Format(TranslationServer.Translate(key), i + 1, size.X, size.Y));
|
||||
}
|
||||
|
||||
_scaleSelect.Clear();
|
||||
foreach (var sc in ScaleOptions)
|
||||
_scaleSelect.AddItem(TranslationServer.Translate($"settings/scale_{sc}x"));
|
||||
_fpsSelect.Clear();
|
||||
foreach (var rate in DisplayOptions.FrameRates)
|
||||
_fpsSelect.AddItem(rate == 0 ? TranslationServer.Translate("settings/max_fps_unlimited") : string.Format(TranslationServer.Translate("settings/max_fps_value"), rate));
|
||||
|
||||
_windowModeSelect.Selected = Mathf.Clamp(previousWindow, 0, 2);
|
||||
_resolutionSelect.Selected = Mathf.Clamp(previousRes, 0, CommonResolutions.Length - 1);
|
||||
_monitorSelect.Selected = Mathf.Clamp(previousMon, 0, count - 1);
|
||||
_scaleSelect.Selected = Mathf.Clamp(previousScale, 0, ScaleOptions.Length - 1);
|
||||
_windowModeSelect.Selected = Mathf.Clamp(mode, 0, DisplayOptions.WindowModes.Length - 1);
|
||||
_monitorSelect.Selected = Mathf.Clamp(monitor, 0, DisplaySettingsService.ScreenCount - 1);
|
||||
_fpsSelect.Selected = Mathf.Clamp(fps, 0, DisplayOptions.FrameRates.Length - 1);
|
||||
}
|
||||
|
||||
private void SelectCurrentResolution(SettingsData s)
|
||||
/// <summary>Fills the window sizes of the chosen monitor, keeping <paramref name="wanted"/> (or the nearest smaller size) selected.</summary>
|
||||
private void FillWindowSizes(Vector2I wanted)
|
||||
{
|
||||
for (var i = 0; i < CommonResolutions.Length; i++)
|
||||
{
|
||||
if (CommonResolutions[i].X == s.ResolutionWidth && CommonResolutions[i].Y == s.ResolutionHeight)
|
||||
{
|
||||
_resolutionSelect.Selected = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
_windowSizes = DisplayOptions.WindowSizes(DisplayServer.ScreenGetSize(Mathf.Max(0, _monitorSelect.Selected)), wanted);
|
||||
_resolutionSelect.Clear();
|
||||
foreach (var size in _windowSizes)
|
||||
_resolutionSelect.AddItem($"{size.X}×{size.Y}");
|
||||
_resolutionSelect.Selected = DisplayOptions.WindowSizeIndex(_windowSizes, wanted);
|
||||
}
|
||||
|
||||
private void SelectCurrentMonitor(SettingsData s)
|
||||
private Vector2I SelectedWindowSize()
|
||||
{
|
||||
var count = DisplayServer.GetScreenCount();
|
||||
if (s.CurrentScreen >= 0 && s.CurrentScreen < count)
|
||||
_monitorSelect.Selected = s.CurrentScreen;
|
||||
var index = _resolutionSelect.Selected;
|
||||
return index >= 0 && index < _windowSizes.Count ? _windowSizes[index] : DisplayServer.WindowGetSize();
|
||||
}
|
||||
|
||||
private void SelectCurrentScale(SettingsData s)
|
||||
private DisplayServer.WindowMode SelectedWindowMode() =>
|
||||
DisplayOptions.WindowModes[Mathf.Clamp(_windowModeSelect.Selected, 0, DisplayOptions.WindowModes.Length - 1)];
|
||||
|
||||
private void UpdateWindowSizeEditable()
|
||||
{
|
||||
for (var i = 0; i < ScaleOptions.Length; i++)
|
||||
{
|
||||
if (ScaleOptions[i] == s.Scale)
|
||||
{
|
||||
_scaleSelect.Selected = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
var windowed = DisplayOptions.UsesWindowSize((int)SelectedWindowMode());
|
||||
_resolutionSelect.Disabled = !windowed;
|
||||
_resHint.Visible = !windowed;
|
||||
}
|
||||
|
||||
private void ApplyAndSave()
|
||||
@@ -287,40 +358,32 @@ namespace Cthangover.BaseMenu
|
||||
s.Save();
|
||||
}
|
||||
|
||||
private void ApplyDisplayAndSave()
|
||||
/// <summary>Saves the window part (mode, monitor, size) and moves the window there.</summary>
|
||||
private void ApplyWindowAndSave()
|
||||
{
|
||||
if (!_initialized) return;
|
||||
var s = GameData.Instance?.Settings;
|
||||
if (s == null) return;
|
||||
|
||||
var windowMode = IndexToWindowMode(_windowModeSelect.Selected);
|
||||
var resIdx = Mathf.Clamp(_resolutionSelect.Selected, 0, CommonResolutions.Length - 1);
|
||||
var resolution = CommonResolutions[resIdx];
|
||||
var monitorIdx = _monitorSelect.Selected;
|
||||
var scaleIdx = Mathf.Clamp(_scaleSelect.Selected, 0, ScaleOptions.Length - 1);
|
||||
var scale = ScaleOptions[scaleIdx];
|
||||
var vsync = _vsyncToggle.ButtonPressed;
|
||||
var size = SelectedWindowSize();
|
||||
s.WindowMode = (int)SelectedWindowMode();
|
||||
s.CurrentScreen = Mathf.Max(0, _monitorSelect.Selected);
|
||||
s.ResolutionWidth = size.X;
|
||||
s.ResolutionHeight = size.Y;
|
||||
DisplaySettingsService.ApplyWindow(s);
|
||||
s.Save();
|
||||
}
|
||||
|
||||
if (monitorIdx >= 0 && monitorIdx < DisplayServer.GetScreenCount())
|
||||
DisplayServer.WindowSetCurrentScreen(monitorIdx);
|
||||
/// <summary>Saves V-Sync and the frame rate cap and applies them.</summary>
|
||||
private void ApplyRenderingAndSave()
|
||||
{
|
||||
if (!_initialized) return;
|
||||
var s = GameData.Instance?.Settings;
|
||||
if (s == null) return;
|
||||
|
||||
DisplayServer.WindowSetMode(windowMode);
|
||||
|
||||
if (windowMode == DisplayServer.WindowMode.Windowed)
|
||||
DisplayServer.WindowSetSize(resolution);
|
||||
|
||||
DisplayServer.WindowSetVsyncMode(vsync
|
||||
? DisplayServer.VSyncMode.Enabled
|
||||
: DisplayServer.VSyncMode.Disabled);
|
||||
|
||||
ProjectSettings.SetSetting("display/window/stretch/scale", (float)scale);
|
||||
|
||||
s.WindowMode = (int)windowMode;
|
||||
s.ResolutionWidth = resolution.X;
|
||||
s.ResolutionHeight = resolution.Y;
|
||||
s.CurrentScreen = monitorIdx;
|
||||
s.VsyncEnabled = vsync;
|
||||
s.Scale = scale;
|
||||
s.VsyncEnabled = _vsyncToggle.ButtonPressed;
|
||||
s.MaxFps = DisplayOptions.FrameRates[Mathf.Clamp(_fpsSelect.Selected, 0, DisplayOptions.FrameRates.Length - 1)];
|
||||
DisplaySettingsService.ApplyRendering(s);
|
||||
s.Save();
|
||||
}
|
||||
|
||||
@@ -332,8 +395,41 @@ namespace Cthangover.BaseMenu
|
||||
private void OnMusicVolumeChanged(double v) { _musicVolumeValue.Text = $"{(int)v}%"; ApplyAndSave(); }
|
||||
private void OnAmbientVolumeChanged(double v) { _ambientVolumeValue.Text = $"{(int)v}%"; ApplyAndSave(); }
|
||||
|
||||
private void OnDisplaySettingChanged(long idx) => ApplyDisplayAndSave();
|
||||
private void OnDisplayToggleChanged(bool on) => ApplyDisplayAndSave();
|
||||
private void OnWindowSettingChanged(long idx)
|
||||
{
|
||||
UpdateWindowSizeEditable();
|
||||
ApplyWindowAndSave();
|
||||
}
|
||||
|
||||
private void OnMonitorSelected(long idx)
|
||||
{
|
||||
FillWindowSizes(SelectedWindowSize());
|
||||
ApplyWindowAndSave();
|
||||
}
|
||||
|
||||
private void OnRenderingSettingChanged(long idx) => ApplyRenderingAndSave();
|
||||
private void OnVsyncToggled(bool on) => ApplyRenderingAndSave();
|
||||
|
||||
/// <summary>Returns the display settings to the game defaults (game_config.json), applies and saves them.</summary>
|
||||
private void OnResetDisplay()
|
||||
{
|
||||
var s = GameData.Instance?.Settings;
|
||||
if (s == null) return;
|
||||
|
||||
var defaults = new SettingsData();
|
||||
s.WindowMode = defaults.WindowMode;
|
||||
s.CurrentScreen = defaults.CurrentScreen;
|
||||
s.ResolutionWidth = defaults.ResolutionWidth;
|
||||
s.ResolutionHeight = defaults.ResolutionHeight;
|
||||
s.VsyncEnabled = defaults.VsyncEnabled;
|
||||
s.MaxFps = defaults.MaxFps;
|
||||
DisplaySettingsService.Apply(s);
|
||||
s.Save();
|
||||
|
||||
_initialized = false;
|
||||
LoadDisplayToUI(s);
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
private void OnLanguageSelected(long idx)
|
||||
{
|
||||
@@ -341,8 +437,8 @@ namespace Cthangover.BaseMenu
|
||||
var s = GameData.Instance?.Settings;
|
||||
if (s == null) return;
|
||||
|
||||
var lang = idx switch { 1 => "en", 2 => "zh", _ => "ru-ru" };
|
||||
if (s.Language == lang) return;
|
||||
var lang = LanguageCode.Supported[Mathf.Clamp((int)idx, 0, LanguageCode.Supported.Length - 1)];
|
||||
if (LanguageCode.Normalize(s.Language) == lang) return;
|
||||
|
||||
s.Language = lang;
|
||||
s.Save();
|
||||
@@ -352,7 +448,11 @@ namespace Cthangover.BaseMenu
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IMenuPanel" />
|
||||
public void Open() => Visible = true;
|
||||
public void Open()
|
||||
{
|
||||
Visible = true;
|
||||
ShowTab(_tab);
|
||||
}
|
||||
|
||||
private void OnBackPressed() => Visible = false;
|
||||
|
||||
@@ -363,26 +463,6 @@ namespace Cthangover.BaseMenu
|
||||
_ambientVolume.Editable = _ambientToggle.ButtonPressed;
|
||||
}
|
||||
|
||||
private static int WindowModeToIndex(int mode)
|
||||
{
|
||||
return mode switch
|
||||
{
|
||||
3 => 1,
|
||||
4 => 2,
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
|
||||
private static DisplayServer.WindowMode IndexToWindowMode(int index)
|
||||
{
|
||||
return index switch
|
||||
{
|
||||
1 => DisplayServer.WindowMode.Fullscreen,
|
||||
2 => DisplayServer.WindowMode.ExclusiveFullscreen,
|
||||
_ => DisplayServer.WindowMode.Windowed,
|
||||
};
|
||||
}
|
||||
|
||||
private void BuildModList()
|
||||
{
|
||||
foreach (var child in _modListContainer.GetChildren())
|
||||
@@ -393,19 +473,25 @@ namespace Cthangover.BaseMenu
|
||||
if (modsWithSettings.Count == 0)
|
||||
return;
|
||||
|
||||
var group = new ButtonGroup();
|
||||
foreach (var modId in modsWithSettings)
|
||||
{
|
||||
var btn = new Button
|
||||
{
|
||||
Text = ModSettingsRegistry.GetDisplayTitle(modId),
|
||||
SizeFlagsHorizontal = Control.SizeFlags.ExpandFill,
|
||||
Alignment = HorizontalAlignment.Left,
|
||||
ToggleMode = true,
|
||||
ButtonGroup = group,
|
||||
FocusMode = FocusModeEnum.None,
|
||||
};
|
||||
CthangoverTheme.ApplyTab(btn);
|
||||
btn.Pressed += () => OnModSelected(modId);
|
||||
_modListContainer.AddChild(btn);
|
||||
}
|
||||
|
||||
var firstMod = modsWithSettings[0];
|
||||
OnModSelected(firstMod);
|
||||
((Button)_modListContainer.GetChild(0)).SetPressedNoSignal(true);
|
||||
OnModSelected(modsWithSettings[0]);
|
||||
}
|
||||
|
||||
private void OnModSelected(string modId)
|
||||
@@ -413,6 +499,7 @@ namespace Cthangover.BaseMenu
|
||||
_selectedModId = modId;
|
||||
_selectedModName.Text = ModSettingsRegistry.GetDisplayTitle(modId);
|
||||
BuildModSettingsPanel(modId);
|
||||
UpdateResetButton();
|
||||
}
|
||||
|
||||
private void BuildModSettingsPanel(string modId)
|
||||
@@ -450,7 +537,7 @@ namespace Cthangover.BaseMenu
|
||||
var label = new Label
|
||||
{
|
||||
Text = TranslationServer.Translate(def.Name),
|
||||
CustomMinimumSize = new Vector2(180, 0),
|
||||
CustomMinimumSize = new Vector2(260, 0),
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
};
|
||||
row.AddChild(label);
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace Mods.CollectionEditTest
|
||||
/// <item>The collection gets the developer toolset (<c>dev_tools</c>: editors,
|
||||
/// the scenario studio, …), saved the way the collection editor saves it (a
|
||||
/// user package with the same id): the running game is now outdated.</item>
|
||||
/// <item>The pause menu's "Main menu" button leaves the game like a player
|
||||
/// does, and the pause menu does not stay above the title screen.</item>
|
||||
/// <item>Menu → Play → New game of the same game switches the mod set in
|
||||
/// place: the editors and the studio are loaded, the studio's button is on
|
||||
/// the game's toolbar and the game matches its collection again.</item>
|
||||
@@ -38,11 +40,13 @@ namespace Mods.CollectionEditTest
|
||||
private const string AddedToolset = "dev_tools";
|
||||
private const string AddedMod = "editor_tools";
|
||||
private const string StudioMod = "scenario_studio";
|
||||
private const string GameMenuId = "game_menu";
|
||||
private const float TimeoutSeconds = 240f;
|
||||
|
||||
private readonly List<string> failures = new();
|
||||
private readonly List<string> enteredScenes = new();
|
||||
private readonly List<(string From, string To)> movedAside = new();
|
||||
private bool startedOnPlayerPackage;
|
||||
private int passed;
|
||||
private int step;
|
||||
private double elapsed;
|
||||
@@ -111,30 +115,60 @@ namespace Mods.CollectionEditTest
|
||||
return;
|
||||
Check($"game '{GameId}' entered ('{enteredScenes[0]}')", true);
|
||||
Check("a freshly entered game matches its collection", !GameCatalog.IsActiveOutdated());
|
||||
Check($"'{AddedMod}' is not loaded before the edit", !EditorsLoaded);
|
||||
MoveUserPackageAside();
|
||||
// A player's own package of the game (say, with the editors added) was what
|
||||
// the game started on: then the editors may already be loaded and New game
|
||||
// need not switch the set. The package is restored at the end.
|
||||
startedOnPlayerPackage = movedAside.Count > 0;
|
||||
if (startedOnPlayerPackage)
|
||||
GameLogger.Log("TEST", "CollectionEditTest: the game started on the player's own package; the before/switch checks are skipped");
|
||||
else
|
||||
Check($"'{AddedMod}' is not loaded before the edit", !EditorsLoaded);
|
||||
|
||||
AddEditorsToCollection();
|
||||
Check("the edited collection makes the running game outdated", GameCatalog.IsActiveOutdated());
|
||||
GetTree().Root.GetNode<GodotSceneService>("GodotSceneService").SwitchToMenu();
|
||||
AppServices.Current.Ui.ToggleEntry(GameMenuId);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
{
|
||||
var open = UiHost.Instance.TryGetInstance(GameMenuId, out var menu) && menu.Visible;
|
||||
Check("the pause menu is open", open);
|
||||
if (menu?.FindChild("MainMenuBtn", true, false) is not Button mainMenu)
|
||||
{
|
||||
failures.Add("the pause menu has no Main menu button");
|
||||
Finish();
|
||||
return;
|
||||
}
|
||||
mainMenu.EmitSignal(BaseButton.SignalName.Pressed);
|
||||
break;
|
||||
}
|
||||
|
||||
case 2:
|
||||
if (Busy || GetTree().CurrentScene is not Cthangover.BaseMenu.MainMenu)
|
||||
return;
|
||||
Check("the pause menu is closed on the title screen",
|
||||
!UiHost.Instance.TryGetInstance(GameMenuId, out var left) || !left.IsVisibleInTree());
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (Busy || !PressMenuPlay())
|
||||
return;
|
||||
enteredScenes.Clear();
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 4:
|
||||
if (!StartFromPicker())
|
||||
return;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case 5:
|
||||
{
|
||||
if (Busy || ActiveGame.Id != GameId || enteredScenes.Count == 0)
|
||||
return;
|
||||
var reload = ModManager.Instance.LastReload;
|
||||
Check("New game after the edit switched the mod set", reload?.Applied == true && reload.Plan?.Load.Contains(AddedMod) == true);
|
||||
if (!startedOnPlayerPackage)
|
||||
Check("New game after the edit switched the mod set", reload?.Applied == true && reload.Plan?.Load.Contains(AddedMod) == true);
|
||||
Check($"'{AddedMod}' is loaded after the edit", EditorsLoaded);
|
||||
Check($"'{StudioMod}' is loaded after the edit", ModRegistry.Instance.Mods.ContainsKey(StudioMod));
|
||||
Check("the studio's button is on the game's toolbar", HasToolbarButton(StudioButtonTooltip()));
|
||||
@@ -154,7 +188,6 @@ namespace Mods.CollectionEditTest
|
||||
/// </summary>
|
||||
private void AddEditorsToCollection()
|
||||
{
|
||||
MoveUserPackageAside();
|
||||
var source = GameCatalog.LoadCollections()[GameId];
|
||||
var edited = JsonSerializer.Deserialize<ModCollection>(JsonSerializer.Serialize(source));
|
||||
edited.Package = source.Package;
|
||||
@@ -168,7 +201,10 @@ namespace Mods.CollectionEditTest
|
||||
|
||||
private void MoveUserPackageAside()
|
||||
{
|
||||
var root = ModCollectionStore.UserPath;
|
||||
// UserPath ends with a separator: without the trim the "parent" is the
|
||||
// collections folder itself, the backup was scanned as a package and
|
||||
// Cleanup's RemoveUser(GameId) deleted the player's package with it.
|
||||
var root = ModCollectionStore.UserPath.TrimEnd('/', '\\');
|
||||
foreach (var path in new[] { Path.Combine(root, GameId), Path.Combine(root, GameId + ".zip") })
|
||||
{
|
||||
var aside = Path.Combine(Path.GetDirectoryName(root) ?? root, $"collection_edit_backup_{Path.GetFileName(path)}");
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Display Settings Test",
|
||||
"description": "Integration test of the Display tab of the settings menu: window mode, window size centred on the monitor, borderless fullscreen, frame rate cap, V-Sync, the restore of the saved file on start and the reset; the player's settings file is restored at the end. Activated only by --itest=display_settings (with --frontend, windowed).",
|
||||
"sources": [
|
||||
"src/*.cs"
|
||||
],
|
||||
"depends": [
|
||||
"menu_base>=1"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Cthangover.BaseMenu;
|
||||
using Cthangover.Core.Bootstrap;
|
||||
using Cthangover.Core.Mods;
|
||||
using Cthangover.Core.Settings;
|
||||
using Cthangover.Core.Utils;
|
||||
using Cthangover.Mods.Interface.View;
|
||||
using Godot;
|
||||
|
||||
namespace Mods.DisplaySettingsTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Display tab of the settings menu driven like a player, one step per tick
|
||||
/// so the window settles: windowed mode with a picked size gives a window of
|
||||
/// that size centred on the monitor; borderless fullscreen disables the size
|
||||
/// list; the frame rate cap and V-Sync are applied; every change is in the
|
||||
/// settings file at once; a "restart" (the file read anew and applied to a
|
||||
/// window put in another state) restores it all; reset returns the game
|
||||
/// defaults. The player's settings file is restored at the end. With a shots
|
||||
/// directory the main menu and every settings tab are saved as PNG. Prints
|
||||
/// <c>[TEST_RESULT] name=display_settings</c>.
|
||||
/// </summary>
|
||||
public partial class DisplaySettingsTestDriverNode : Node
|
||||
{
|
||||
private const string Display = "Window/Column/Body/Pages/Display/";
|
||||
private const float TimeoutSeconds = 60f;
|
||||
private const string ResetButton = "Window/Column/Footer/Row/ResetButton";
|
||||
private static readonly Vector2I WindowSize = new(1280, 720);
|
||||
|
||||
private readonly List<string> failures = new();
|
||||
private readonly string settingsPath = ProjectSettings.GlobalizePath("user://settings.json");
|
||||
private readonly string backup;
|
||||
private readonly string shotsDir;
|
||||
private SettingsMenu menu;
|
||||
private int passed;
|
||||
private int step;
|
||||
private int shotTab;
|
||||
private double elapsed;
|
||||
private double stepAt;
|
||||
private bool done;
|
||||
|
||||
public DisplaySettingsTestDriverNode(string shotsDir)
|
||||
{
|
||||
this.shotsDir = shotsDir;
|
||||
backup = File.Exists(settingsPath) ? File.ReadAllText(settingsPath) : null;
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
if (done)
|
||||
return;
|
||||
|
||||
elapsed += delta;
|
||||
if (elapsed > TimeoutSeconds)
|
||||
{
|
||||
failures.Add($"timeout at step {step}");
|
||||
Finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (elapsed - stepAt < 0.5)
|
||||
return;
|
||||
stepAt = elapsed;
|
||||
|
||||
try
|
||||
{
|
||||
RunStep();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
failures.Add($"step {step}: {ex.GetType().Name}: {ex.Message}");
|
||||
Finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void RunStep()
|
||||
{
|
||||
switch (step)
|
||||
{
|
||||
case 0:
|
||||
if (!Boot.IsReady)
|
||||
return;
|
||||
if (GetTree().CurrentScene?.FindChild("LauncherPlay", true, false) is Button launcherPlay)
|
||||
{
|
||||
launcherPlay.EmitSignal(BaseButton.SignalName.Pressed);
|
||||
return;
|
||||
}
|
||||
if (GetTree().CurrentScene is not MainMenu main)
|
||||
return;
|
||||
menu = main.GetNodeOrNull<SettingsMenu>("SettingsPanel");
|
||||
Check("settings panel exists", menu != null);
|
||||
if (menu == null)
|
||||
{
|
||||
Finish();
|
||||
return;
|
||||
}
|
||||
Shot("main_menu");
|
||||
menu.Open();
|
||||
shotTab = 0;
|
||||
step = 100;
|
||||
return;
|
||||
|
||||
case 100:
|
||||
// One tick per settings tab for the screenshots, then the Display tab checks.
|
||||
if (shotTab > 0)
|
||||
Shot("settings_" + shotTab);
|
||||
if (shotTab < 4 && !string.IsNullOrEmpty(shotsDir))
|
||||
{
|
||||
menu.ShowTab(shotTab++);
|
||||
return;
|
||||
}
|
||||
menu.ShowTab(2);
|
||||
Check("the Display tab shows its page and the display reset",
|
||||
menu.GetNode<Control>("Window/Column/Body/Pages/Display").Visible && !menu.GetNode<Control>("Window/Column/Body/Pages/Audio").Visible
|
||||
&& menu.GetNode<Button>(ResetButton).Visible);
|
||||
var monitor = menu.GetNode<OptionButton>(Display + "MonitorRow/MonitorSelect");
|
||||
var primary = DisplayServer.GetPrimaryScreen();
|
||||
var screen = DisplayServer.ScreenGetSize(primary);
|
||||
Check("monitors are listed, the primary one marked",
|
||||
monitor.ItemCount == DisplayServer.GetScreenCount()
|
||||
&& monitor.GetItemText(primary) == string.Format(TranslationServer.Translate("settings/monitor_screen_primary"), primary + 1, screen.X, screen.Y));
|
||||
Pick("ModeRow/ModeSelect", 0);
|
||||
step = 1;
|
||||
return;
|
||||
|
||||
case 1:
|
||||
var sizes = menu.GetNode<OptionButton>(Display + "ResRow/ResSelect");
|
||||
Check("windowed mode enables the size list and hides the hint", !sizes.Disabled && !menu.GetNode<Label>(Display + "ResHint").Visible);
|
||||
var index = ItemIndex(sizes, $"{WindowSize.X}×{WindowSize.Y}");
|
||||
Check("size list offers 1280×720", index >= 0);
|
||||
Pick("ResRow/ResSelect", index);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
{
|
||||
var area = DisplayServer.ScreenGetUsableRect(DisplayServer.WindowGetCurrentScreen());
|
||||
Check($"window is windowed ({DisplayServer.WindowGetMode()})", DisplayServer.WindowGetMode() == DisplayServer.WindowMode.Windowed);
|
||||
Check($"window has the picked size ({DisplayServer.WindowGetSize()})", DisplayServer.WindowGetSize() == WindowSize);
|
||||
Check($"window is centred ({DisplayServer.WindowGetPosition()} in {area})", DisplayServer.WindowGetPosition() == area.Position + (area.Size - WindowSize) / 2);
|
||||
var saved = Saved();
|
||||
Check("windowed mode and size are saved",
|
||||
saved.WindowMode == (int)DisplayServer.WindowMode.Windowed && saved.ResolutionWidth == WindowSize.X && saved.ResolutionHeight == WindowSize.Y);
|
||||
Pick("ModeRow/ModeSelect", DisplayOptions.WindowModeIndex((int)DisplayServer.WindowMode.Fullscreen));
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
Check($"borderless fullscreen is on ({DisplayServer.WindowGetMode()})", DisplayServer.WindowGetMode() == DisplayServer.WindowMode.Fullscreen);
|
||||
Check("fullscreen disables the size list and shows the hint",
|
||||
menu.GetNode<OptionButton>(Display + "ResRow/ResSelect").Disabled && menu.GetNode<Label>(Display + "ResHint").Visible);
|
||||
Check("fullscreen mode is saved, the window size kept", Saved().WindowMode == (int)DisplayServer.WindowMode.Fullscreen && Saved().ResolutionWidth == WindowSize.X);
|
||||
Pick("FpsRow/FpsSelect", DisplayOptions.FrameRateIndex(60));
|
||||
var vsync = menu.GetNode<CheckButton>(Display + "VSyncRow/VSyncToggle");
|
||||
vsync.ButtonPressed = !vsync.ButtonPressed;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
{
|
||||
var vsyncOn = menu.GetNode<CheckButton>(Display + "VSyncRow/VSyncToggle").ButtonPressed;
|
||||
Check($"frame rate cap applied ({Godot.Engine.MaxFps})", Godot.Engine.MaxFps == 60);
|
||||
var saved = Saved();
|
||||
Check("frame rate cap and V-Sync are saved", saved.MaxFps == 60 && saved.VsyncEnabled == vsyncOn);
|
||||
// "Restart": the window in another state (settled for a tick), then the start-up restore from the file.
|
||||
DisplayServer.WindowSetMode(DisplayServer.WindowMode.Windowed);
|
||||
DisplayServer.WindowSetSize(new Vector2I(800, 600));
|
||||
Godot.Engine.MaxFps = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case 5:
|
||||
Check($"window left fullscreen before the restore ({DisplayServer.WindowGetMode()})", DisplayServer.WindowGetMode() == DisplayServer.WindowMode.Windowed);
|
||||
DisplaySettingsService.Apply(Saved());
|
||||
break;
|
||||
|
||||
case 6:
|
||||
Check($"restore from the file brings fullscreen back ({DisplayServer.WindowGetMode()})", DisplayServer.WindowGetMode() == DisplayServer.WindowMode.Fullscreen);
|
||||
Check("restore from the file brings the frame rate cap back", Godot.Engine.MaxFps == 60);
|
||||
menu.GetNode<Button>(ResetButton).EmitSignal(BaseButton.SignalName.Pressed);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
{
|
||||
var defaults = new SettingsData();
|
||||
var saved = Saved();
|
||||
Check("reset saves the game defaults",
|
||||
saved.WindowMode == defaults.WindowMode && saved.ResolutionWidth == defaults.ResolutionWidth
|
||||
&& saved.ResolutionHeight == defaults.ResolutionHeight && saved.MaxFps == defaults.MaxFps && saved.VsyncEnabled == defaults.VsyncEnabled);
|
||||
Check("reset shows the default mode",
|
||||
menu.GetNode<OptionButton>(Display + "ModeRow/ModeSelect").Selected == DisplayOptions.WindowModeIndex(defaults.WindowMode));
|
||||
Check($"reset applies the default frame rate cap ({Godot.Engine.MaxFps})", Godot.Engine.MaxFps == defaults.MaxFps);
|
||||
Finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
step++;
|
||||
}
|
||||
|
||||
private void Shot(string name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(shotsDir) || DisplayServer.GetName() == "headless")
|
||||
return;
|
||||
var dir = ProjectSettings.GlobalizePath(shotsDir);
|
||||
Directory.CreateDirectory(dir);
|
||||
var path = Path.Combine(dir, name + ".png");
|
||||
ViewportCapture.SavePng(GetViewport(), path);
|
||||
GameLogger.Log("TEST", $"DisplaySettingsTest: screenshot {path}");
|
||||
}
|
||||
|
||||
/// <summary>Selects item <paramref name="index"/> of a Display tab list the way a click does.</summary>
|
||||
private void Pick(string path, int index)
|
||||
{
|
||||
var option = menu.GetNode<OptionButton>(Display + path);
|
||||
option.Select(index);
|
||||
option.EmitSignal(OptionButton.SignalName.ItemSelected, (long)index);
|
||||
}
|
||||
|
||||
private static int ItemIndex(OptionButton option, string text)
|
||||
{
|
||||
for (var i = 0; i < option.ItemCount; i++)
|
||||
{
|
||||
if (option.GetItemText(i) == text)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>The settings as the next start reads them from the file.</summary>
|
||||
private static SettingsData Saved()
|
||||
{
|
||||
var settings = new SettingsData();
|
||||
settings.Load();
|
||||
return settings;
|
||||
}
|
||||
|
||||
private void Check(string name, bool ok)
|
||||
{
|
||||
if (ok)
|
||||
{
|
||||
passed++;
|
||||
GameLogger.Log("TEST", $"DisplaySettingsTest: [PASS] {name}");
|
||||
}
|
||||
else
|
||||
{
|
||||
failures.Add(name);
|
||||
}
|
||||
}
|
||||
|
||||
private void Finish()
|
||||
{
|
||||
if (done)
|
||||
return;
|
||||
done = true;
|
||||
|
||||
if (backup != null)
|
||||
File.WriteAllText(settingsPath, backup);
|
||||
else if (File.Exists(settingsPath))
|
||||
File.Delete(settingsPath);
|
||||
GameData.Instance?.Settings?.Load();
|
||||
|
||||
foreach (var failure in failures)
|
||||
GameLogger.Log("TEST", $"DisplaySettingsTest: failed: {failure}");
|
||||
GameLogger.Log("TEST", $"[TEST_RESULT] name=display_settings passed={passed} failed={failures.Count}");
|
||||
GetTree().Quit(failures.Count == 0 ? 0 : 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Linq;
|
||||
using Cthangover.Core.Mods;
|
||||
using Cthangover.Core.Utils;
|
||||
using Godot;
|
||||
|
||||
namespace Mods.DisplaySettingsTest
|
||||
{
|
||||
/// <summary>Queues <see cref="DisplaySettingsTestDriverNode"/> only for <c>--itest=display_settings</c>; <c>--shots=<dir></c> makes it save screenshots of the menu and every settings tab.</summary>
|
||||
public class DisplaySettingsTestInitializer : IModInitializer
|
||||
{
|
||||
private static bool queued;
|
||||
|
||||
public void OnModLoaded(string modId) { }
|
||||
|
||||
public void OnModResourcesReady()
|
||||
{
|
||||
var args = OS.GetCmdlineUserArgs();
|
||||
if (queued || !args.Contains("--itest=display_settings"))
|
||||
return;
|
||||
|
||||
string shots = null;
|
||||
foreach (var arg in args)
|
||||
{
|
||||
if (arg.StartsWith("--shots="))
|
||||
shots = arg.Substring("--shots=".Length);
|
||||
}
|
||||
|
||||
queued = true;
|
||||
GameLogger.Log("TEST", "DisplaySettingsTest: activating");
|
||||
(Godot.Engine.GetMainLoop() as SceneTree)?.Root?.CallDeferred("add_child", new DisplaySettingsTestDriverNode(shots));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ using Cthangover.Core.Games;
|
||||
using Cthangover.Core.Mods;
|
||||
using Cthangover.Core.Settings;
|
||||
using Cthangover.Core.Utils;
|
||||
using Cthangover.Mods.Interface.View;
|
||||
using Godot;
|
||||
|
||||
namespace Mods.MenuFlowTest
|
||||
@@ -19,7 +20,8 @@ namespace Mods.MenuFlowTest
|
||||
/// without a game); Play opens a card per game; the save browser lists a
|
||||
/// game's save; a player's own game can be deleted from the collection
|
||||
/// editor (a built-in one cannot) while its mods stay; the composition of
|
||||
/// a game is computed without loading it; Continue sees the last session. Prints <c>[TEST_RESULT] name=menu_flow</c>.
|
||||
/// a game is computed without loading it; Continue sees the last session.
|
||||
/// <c>--shots=<dir></c> saves the game picker and the save browser. Prints <c>[TEST_RESULT] name=menu_flow</c>.
|
||||
/// </summary>
|
||||
public partial class MenuFlowTestDriverNode : Node
|
||||
{
|
||||
@@ -96,6 +98,7 @@ namespace Mods.MenuFlowTest
|
||||
{
|
||||
var picker = Menu.GetNodeOrNull<GamePickerPanel>("GamePickerPanel");
|
||||
Check("Play opens the game picker", picker?.Visible == true);
|
||||
Shot("game_picker");
|
||||
var games = GameCatalog.Games();
|
||||
var newGameText = TranslationServer.Translate("menu/play/new_game").ToString();
|
||||
var startButtons = Buttons(picker).Where(b => b.Text == newGameText).ToList();
|
||||
@@ -155,6 +158,7 @@ namespace Mods.MenuFlowTest
|
||||
var slots = browser.FindChildren("*", "", true, false).OfType<SaveSlotItem>().ToList();
|
||||
Check("save browser lists the game's save", slots.Any(s => s.Model?.FileName == Slot));
|
||||
Check("save list carries the game id", SaveService.ListSaves(GameId).Any(s => s.FileName == Slot && s.GameId == GameId));
|
||||
Shot("save_browser");
|
||||
browser.Visible = false;
|
||||
|
||||
var composition = GameCatalog.Composition(GameId);
|
||||
@@ -175,6 +179,22 @@ namespace Mods.MenuFlowTest
|
||||
step++;
|
||||
}
|
||||
|
||||
/// <summary>With <c>--shots=<dir></c> saves the window as <paramref name="name"/>.png (windowed runs only).</summary>
|
||||
private void Shot(string name)
|
||||
{
|
||||
string dir = null;
|
||||
foreach (var arg in OS.GetCmdlineUserArgs())
|
||||
{
|
||||
if (arg.StartsWith("--shots="))
|
||||
dir = arg.Substring("--shots=".Length);
|
||||
}
|
||||
if (string.IsNullOrEmpty(dir) || DisplayServer.GetName() == "headless")
|
||||
return;
|
||||
dir = ProjectSettings.GlobalizePath(dir);
|
||||
Directory.CreateDirectory(dir);
|
||||
ViewportCapture.SavePng(GetViewport(), Path.Combine(dir, name + ".png"));
|
||||
}
|
||||
|
||||
private Button DeleteButton()
|
||||
{
|
||||
var text = TranslationServer.Translate("mod_manager/collection_delete").ToString();
|
||||
|
||||
@@ -17,7 +17,8 @@ namespace Mods.NewGameTest
|
||||
/// Plays the games the way a player does, in ONE process: no restart
|
||||
/// happens anywhere, every game switch replaces the mod set in place.
|
||||
/// <list type="number">
|
||||
/// <item>Front-end menu → Play → the <c>cthangover</c> card → New game: the
|
||||
/// <item>The front-end menu warms every game's mods up in the background;
|
||||
/// once done, Play → the <c>cthangover</c> card → New game: the
|
||||
/// process switches to the game, enters <c>prolog_alley</c> and the opening
|
||||
/// dialog about the hungover Marao is clicked through.</item>
|
||||
/// <item>Hot reload inside the game (forced reload of <c>cthangover_party</c>):
|
||||
@@ -105,8 +106,13 @@ namespace Mods.NewGameTest
|
||||
switch (step)
|
||||
{
|
||||
case 0:
|
||||
if (!Boot.IsReady || !PressMenuPlay())
|
||||
// The menu compiles the games' mods in the background; the switch
|
||||
// below must then only load them from the cache (see the log).
|
||||
if (!Boot.IsReady || GetTree().CurrentScene is not Cthangover.BaseMenu.MainMenu || GameSession.IsWarmingUp)
|
||||
return;
|
||||
if (!PressMenuPlay())
|
||||
return;
|
||||
Check("the menu warmed the games up in the background", true);
|
||||
Check("front-end menu offers Play", ActiveGame.IsFrontendOnly);
|
||||
break;
|
||||
|
||||
|
||||
Reference in new issue
Block a user