Line endings: LF everywhere

.gitattributes forces LF (CRLF only for .bat/.cmd), core.autocrlf is off;
files that differed only by CRLF are normalized.
This commit is contained in:
cthangover committed 2026-10-21 13:29:46 +02:00
1 parent b8fb306a57
commit b3ac8aec8f
3 files changed
+236 -206

No files matched your search

+30
View File
@@ -0,0 +1,30 @@
# Line endings: LF everywhere, on every OS, whatever core.autocrlf says.
* text=auto eol=lf
# cmd.exe misreads labels and goto in LF batch files, so they stay CRLF.
*.bat text eol=crlf
*.cmd text eol=crlf
# Binaries: never converted, never diffed as text.
*.png binary
*.jpg binary
*.jpeg binary
*.webp binary
*.svg text
*.ogg binary
*.wav binary
*.mp3 binary
*.ttf binary
*.otf binary
*.woff binary
*.woff2 binary
*.dll binary
*.so binary
*.dylib binary
*.pdb binary
*.exe binary
*.zip binary
*.pck binary
*.moc3 binary
*.res binary
*.scn binary
+37 -37
View File
@@ -1,37 +1,37 @@
# Godot 4.x # Godot 4.x
.godot/ .godot/
.idea/ .idea/
sdk/ sdk/
bin/ bin/
obj/ obj/
#ai #ai
.omo/ .omo/
.opencode/ .opencode/
gen/ gen/
obj/ obj/
bin/ bin/
*.tres~ *.tres~
*.tscn~ *.tscn~
# GDScript LSP # GDScript LSP
.godot/ .godot/
# Export # Export
*.exe *.exe
*.dmg *.dmg
*.apk *.apk
*.aab *.aab
*.pck *.pck
*.zip *.zip
*.log *.log
# OS # OS
.DS_Store .DS_Store
Thumbs.db Thumbs.db
# Editor # Editor
*.import *.import
*.godot.uid *.godot.uid
+169 -169
View File
@@ -1,169 +1,169 @@
using System.Collections.Generic; using System.Collections.Generic;
using Cthangover.Core.Scenes; using Cthangover.Core.Scenes;
using Cthangover.Core.Settings; using Cthangover.Core.Settings;
using Cthangover.Core.UI.Menu; using Cthangover.Core.UI.Menu;
using Cthangover.Core.UI.Theming; using Cthangover.Core.UI.Theming;
using Cthangover.Core.UI.Tool; using Cthangover.Core.UI.Tool;
using Cthangover.Core.Utils; using Cthangover.Core.Utils;
using Godot; using Godot;
using Cthangover.Mods.Interface.Menu; using Cthangover.Mods.Interface.Menu;
namespace Cthangover.BaseMenu namespace Cthangover.BaseMenu
{ {
/// <summary> /// <summary>
/// Title screen main menu, fully driven by <see cref="MenuRegistry"/>. The scene /// Title screen main menu, fully driven by <see cref="MenuRegistry"/>. The scene
/// only defines named section containers (game, mods, exit, tools); every button — /// only defines named section containers (game, mods, exit, tools); every button —
/// built-in or mod-provided — is created from registry entries, so mods can add, /// built-in or mod-provided — is created from registry entries, so mods can add,
/// replace or re-order entries without touching this scene. Below the sections, /// replace or re-order entries without touching this scene. Below the sections,
/// every registered tool appends its own launcher button via /// every registered tool appends its own launcher button via
/// <see cref="IToolProvider.AddMenuButton"/>, so no tool list is hardcoded here. /// <see cref="IToolProvider.AddMenuButton"/>, so no tool list is hardcoded here.
/// If GameLogger reports mod compilation errors, a popup lists them. The /// If GameLogger reports mod compilation errors, a popup lists them. The
/// SettingsMenu and SaveLoadMenu panels are instantiated at ready time but stay /// SettingsMenu and SaveLoadMenu panels are instantiated at ready time but stay
/// hidden; they are registered in <see cref="MenuRegistry"/> as panels "settings" /// hidden; they are registered in <see cref="MenuRegistry"/> as panels "settings"
/// and "save_load", so other mods can open them by id. /// and "save_load", so other mods can open them by id.
/// </summary> /// </summary>
public partial class MainMenu : Control public partial class MainMenu : Control
{ {
private SettingsMenu _settingsMenu; private SettingsMenu _settingsMenu;
private SaveLoadMenu _saveLoadMenu; private SaveLoadMenu _saveLoadMenu;
private GamePickerPanel _gamePicker; private GamePickerPanel _gamePicker;
private readonly Dictionary<Button, string> _btnKeys = new(); private readonly Dictionary<Button, string> _btnKeys = new();
public override void _Ready() public override void _Ready()
{ {
try try
{ {
BuildUi(); BuildUi();
} }
catch (System.Exception ex) catch (System.Exception ex)
{ {
GameLogger.Log("MAIN_MENU", GameLogger.Log("MAIN_MENU",
$"_Ready failed: {ex.GetType().Name}: {ex.Message}{System.Environment.NewLine}{ex.StackTrace}", $"_Ready failed: {ex.GetType().Name}: {ex.Message}{System.Environment.NewLine}{ex.StackTrace}",
LogLevel.Error); LogLevel.Error);
throw; throw;
} }
} }
/// <summary> /// <summary>
/// Assembles the runtime UI: theme, hidden settings and save/load panels, /// Assembles the runtime UI: theme, hidden settings and save/load panels,
/// telegram link wiring, scene manager initialization and registry/tool /// telegram link wiring, scene manager initialization and registry/tool
/// driven buttons. /// driven buttons.
/// </summary> /// </summary>
private void BuildUi() private void BuildUi()
{ {
CthangoverTheme.Apply(this); CthangoverTheme.Apply(this);
_settingsMenu = new ModScenePrefab("scenes/menu/settings_menu.tscn").Instantiate<SettingsMenu>(); _settingsMenu = new ModScenePrefab("scenes/menu/settings_menu.tscn").Instantiate<SettingsMenu>();
_settingsMenu.Name = "SettingsPanel"; _settingsMenu.Name = "SettingsPanel";
_settingsMenu.Visible = false; _settingsMenu.Visible = false;
_settingsMenu.LanguageChanged += RefreshButtons; _settingsMenu.LanguageChanged += RefreshButtons;
AddChild(_settingsMenu); AddChild(_settingsMenu);
_saveLoadMenu = new ModScenePrefab("scenes/menu/save_load_menu.tscn").Instantiate<SaveLoadMenu>(); _saveLoadMenu = new ModScenePrefab("scenes/menu/save_load_menu.tscn").Instantiate<SaveLoadMenu>();
_saveLoadMenu.Name = "SaveLoadPanel"; _saveLoadMenu.Name = "SaveLoadPanel";
_saveLoadMenu.Visible = false; _saveLoadMenu.Visible = false;
AddChild(_saveLoadMenu); AddChild(_saveLoadMenu);
_gamePicker = new GamePickerPanel { Name = "GamePickerPanel" }; _gamePicker = new GamePickerPanel { Name = "GamePickerPanel" };
AddChild(_gamePicker); AddChild(_gamePicker);
_gamePicker.SavesRequested += gameId => _gamePicker.SavesRequested += gameId =>
{ {
_gamePicker.Visible = false; _gamePicker.Visible = false;
_saveLoadMenu.OpenForGame(gameId); _saveLoadMenu.OpenForGame(gameId);
}; };
var telegramLink = GetNode<RichTextLabel>("TelegramLink"); var telegramLink = GetNode<RichTextLabel>("TelegramLink");
telegramLink.MetaClicked += OnTelegramLinkClicked; telegramLink.MetaClicked += OnTelegramLinkClicked;
var sceneManager = GetNode<SceneManager>("/root/SceneManager"); var sceneManager = GetNode<SceneManager>("/root/SceneManager");
sceneManager.Initialize(); sceneManager.Initialize();
BuildRegistryButtons(); BuildRegistryButtons();
BuildToolButtons(); BuildToolButtons();
CheckModCompilationErrors(); CheckModCompilationErrors();
RefreshButtons(); RefreshButtons();
} }
/// <summary> /// <summary>
/// Creates one button per <see cref="MenuRegistry"/> entry, routed to the /// Creates one button per <see cref="MenuRegistry"/> entry, routed to the
/// section container matching its <c>Section</c> ("game", "mods", "exit", /// section container matching its <c>Section</c> ("game", "mods", "exit",
/// "tools"). Runs after <see cref="SceneManager.Initialize"/> because the /// "tools"). Runs after <see cref="SceneManager.Initialize"/> because the
/// registry is built during boot and filled by mod initializers; the /// registry is built during boot and filled by mod initializers; the
/// defensive Initialize call keeps direct scene launches working. Each /// defensive Initialize call keeps direct scene launches working. Each
/// button's translation key is registered so <see cref="RefreshButtons"/> /// button's translation key is registered so <see cref="RefreshButtons"/>
/// keeps it localized. /// keeps it localized.
/// </summary> /// </summary>
private void BuildRegistryButtons() private void BuildRegistryButtons()
{ {
var sections = new Dictionary<string, Container> var sections = new Dictionary<string, Container>
{ {
{ "game", GetNode<Container>("MenuContainer/GameButtons") }, { "game", GetNode<Container>("MenuContainer/GameButtons") },
{ MenuButtonEntry.SectionMods, GetNode<Container>("MenuContainer/ModMenuButtons") }, { MenuButtonEntry.SectionMods, GetNode<Container>("MenuContainer/ModMenuButtons") },
{ "exit", GetNode<Container>("MenuContainer/ExitButtons") }, { "exit", GetNode<Container>("MenuContainer/ExitButtons") },
{ "tools", GetNode<Container>("MenuContainer/ToolMenuButtons") }, { "tools", GetNode<Container>("MenuContainer/ToolMenuButtons") },
}; };
MenuBuilder.Build(this, sections); MenuBuilder.Build(this, sections);
foreach (var container in sections.Values) foreach (var container in sections.Values)
MenuBuilder.CollectLocaleKeys(container, _btnKeys); MenuBuilder.CollectLocaleKeys(container, _btnKeys);
} }
/// <summary> /// <summary>
/// Lets every registered tool append its launcher button to the tool block /// Lets every registered tool append its launcher button to the tool block
/// below Exit (<c>MenuContainer/ToolMenuButtons</c>). Tools register /// below Exit (<c>MenuContainer/ToolMenuButtons</c>). Tools register
/// themselves in <see cref="ToolFactory"/>, so the block stays empty when no /// themselves in <see cref="ToolFactory"/>, so the block stays empty when no
/// tools are installed and needs no changes when a mod introduces a new tool. /// tools are installed and needs no changes when a mod introduces a new tool.
/// Each button carries its translation key in metadata, keeping /// Each button carries its translation key in metadata, keeping
/// <see cref="RefreshButtons"/> aware of it. /// <see cref="RefreshButtons"/> aware of it.
/// </summary> /// </summary>
private void BuildToolButtons() private void BuildToolButtons()
{ {
var container = GetNodeOrNull<VBoxContainer>("MenuContainer/ToolMenuButtons"); var container = GetNodeOrNull<VBoxContainer>("MenuContainer/ToolMenuButtons");
if (container == null) if (container == null)
return; return;
foreach (var tool in ToolFactory.Instance.GetAll()) foreach (var tool in ToolFactory.Instance.GetAll())
tool.AddMenuButton(container); tool.AddMenuButton(container);
MenuBuilder.CollectLocaleKeys(container, _btnKeys); MenuBuilder.CollectLocaleKeys(container, _btnKeys);
GameLogger.Log("MAIN_MENU", $"Built {container.GetChildCount()} tool button(s)"); GameLogger.Log("MAIN_MENU", $"Built {container.GetChildCount()} tool button(s)");
} }
private void CheckModCompilationErrors() private void CheckModCompilationErrors()
{ {
if (GameLogger.CompilationErrors.Count == 0) if (GameLogger.CompilationErrors.Count == 0)
return; return;
var errorText = string.Join("\r\n", GameLogger.CompilationErrors); var errorText = string.Join("\r\n", GameLogger.CompilationErrors);
ShowModErrorPopup(errorText); ShowModErrorPopup(errorText);
} }
private void RefreshButtons() private void RefreshButtons()
{ {
foreach (var kv in _btnKeys) foreach (var kv in _btnKeys)
kv.Key.Text = TranslationServer.Translate(kv.Value); kv.Key.Text = TranslationServer.Translate(kv.Value);
} }
private void ShowModErrorPopup(string errorText) private void ShowModErrorPopup(string errorText)
{ {
var dialog = new AcceptDialog(); var dialog = new AcceptDialog();
dialog.Title = "Mod Compilation Error"; dialog.Title = "Mod Compilation Error";
dialog.DialogText = errorText; dialog.DialogText = errorText;
dialog.OkButtonText = "OK"; dialog.OkButtonText = "OK";
dialog.Exclusive = true; dialog.Exclusive = true;
AddChild(dialog); AddChild(dialog);
dialog.PopupCentered(); dialog.PopupCentered();
} }
private void OnTelegramLinkClicked(Variant meta) private void OnTelegramLinkClicked(Variant meta)
{ {
OS.ShellOpen($"https://{meta.AsString()}"); OS.ShellOpen($"https://{meta.AsString()}");
} }
} }
} }