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