Game picker and save browser: menu window shell, background warm-up
The main menu warms every ready game's mods up into the assembly cache in the background (last played first), so picking a game only loads cached assemblies. Game picker and save browser are scenes on the shared MenuWindowShell (also used by the settings): caps title, close on the right, footer hint; the picker footer shows the warm-up state, cards get framed covers with a letter placeholder and Continue as the main action; the save browser shows 3x2 large cards with the game selector in the header. new_game waits for the warm-up; menu_flow takes --shots.
This commit is contained in:
1 parent
9bd0483af9
commit
f04a2a3661
13 files changed
+500
-210
No files matched your search
@@ -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.
|
||||
|
||||
@@ -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 = Это сохранение сделано до появления игр-коллекций. Выберите игру, к которой оно относится, — файл переедет в её сохранения.
|
||||
|
||||
@@ -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
|
||||
+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)
|
||||
|
||||
+26
-1
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using Cthangover.Core.Games;
|
||||
using Cthangover.Core.Scenes;
|
||||
using Cthangover.Core.Settings;
|
||||
using Cthangover.Core.UI;
|
||||
@@ -70,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 =>
|
||||
{
|
||||
@@ -89,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;
|
||||
|
||||
+5
-26
@@ -35,7 +35,7 @@ namespace Cthangover.BaseMenu
|
||||
/// refresh their translated button text.
|
||||
/// </summary>
|
||||
public event Action LanguageChanged;
|
||||
private const string Header = "Window/Column/Header/Row/";
|
||||
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/";
|
||||
@@ -81,20 +81,8 @@ namespace Cthangover.BaseMenu
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
CthangoverTheme.Apply(this);
|
||||
GetNode<ColorRect>("Backdrop").Color = UiTokens.Backdrop;
|
||||
GetNode<PanelContainer>("Window").AddThemeStyleboxOverride("panel", UiKit.Frame(UiTokens.RadiusLg, UiTokens.Surface0, UiTokens.Line));
|
||||
GetNode<ColorRect>("Window/Column/RuleHeader").Color = UiTokens.Line;
|
||||
GetNode<ColorRect>("Window/Column/RuleFooter").Color = UiTokens.Line;
|
||||
|
||||
var title = GetNode<Label>(Header + "Title");
|
||||
title.AddThemeFontSizeOverride("font_size", UiTokens.FontTitle);
|
||||
title.AddThemeColorOverride("font_color", UiTokens.InkBright);
|
||||
RegisterLabel(title, "menu/settings");
|
||||
|
||||
var close = GetNode<Button>(Header + "CloseButton");
|
||||
CthangoverTheme.ApplyIcon(close);
|
||||
close.Pressed += OnBackPressed;
|
||||
MenuWindowShell.Style(this, OnBackPressed);
|
||||
RegisterLabel(GetNode<Label>(Header + "Title"), "menu/settings");
|
||||
|
||||
_tabButtons = new[]
|
||||
{
|
||||
@@ -138,7 +126,7 @@ namespace Cthangover.BaseMenu
|
||||
_fpsSelect = GetNode<OptionButton>(Display + "FpsRow/FpsSelect");
|
||||
_vsyncToggle = GetNode<CheckButton>(Display + "VSyncRow/VSyncToggle");
|
||||
_resHint = GetNode<Label>(Display + "ResHint");
|
||||
StyleHint(_resHint);
|
||||
MenuWindowShell.StyleHint(_resHint);
|
||||
|
||||
GetNode<PanelContainer>(Mods + "ModListPanel").AddThemeStyleboxOverride("panel", ListPanelStyle());
|
||||
_modListContainer = GetNode<VBoxContainer>(Mods + "ModListPanel/Column/ModScroll/ModList");
|
||||
@@ -151,7 +139,6 @@ namespace Cthangover.BaseMenu
|
||||
CthangoverTheme.ApplyDanger(_resetButton);
|
||||
_resetButton.Pressed += OnResetPressed;
|
||||
_footerHint = GetNode<Label>("Window/Column/Footer/Row/Hint");
|
||||
StyleHint(_footerHint);
|
||||
RegisterLabel(_footerHint, "settings/saved_at_once");
|
||||
|
||||
RegisterSection(Pages + "General/LangSection", "settings/language");
|
||||
@@ -233,17 +220,10 @@ namespace Cthangover.BaseMenu
|
||||
private void RegisterSection(string path, string key)
|
||||
{
|
||||
var label = GetNode<Label>(path);
|
||||
label.AddThemeFontSizeOverride("font_size", UiTokens.FontSmall);
|
||||
label.AddThemeColorOverride("font_color", UiTokens.Muted);
|
||||
MenuWindowShell.StyleSection(label);
|
||||
RegisterLabel(label, key);
|
||||
}
|
||||
|
||||
private static void StyleHint(Label label)
|
||||
{
|
||||
label.AddThemeFontSizeOverride("font_size", UiTokens.FontBodySmall);
|
||||
label.AddThemeColorOverride("font_color", UiTokens.Faint);
|
||||
}
|
||||
|
||||
private static StyleBoxFlat ListPanelStyle()
|
||||
{
|
||||
var style = UiKit.Frame(UiTokens.RadiusMd, UiTokens.Surface2, UiTokens.Line);
|
||||
@@ -261,7 +241,6 @@ namespace Cthangover.BaseMenu
|
||||
}
|
||||
for (var i = 0; i < _tabButtons.Length; i++)
|
||||
_tabButtons[i].Text = TranslationServer.Translate(TabKeys[i]);
|
||||
GetNode<Button>(Header + "CloseButton").TooltipText = TranslationServer.Translate("settings/back");
|
||||
UpdateResetButton();
|
||||
PopulateLangOptions();
|
||||
PopulateDisplayOptions();
|
||||
|
||||
@@ -19,7 +19,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 +97,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 +157,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 +178,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);
|
||||
GetViewport().GetTexture().GetImage().SavePng(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