Mod naming rule and engine UI in engine mods

Mods are renamed by role (shell2d_base, menu_base, ui_base, live2d_base,
editor_tools, cthangover_story, cthangover_murakami, cthangover_party) and
every reference follows. Engine UI prefabs (dialog, answers, message, game
menu, inventory, map, skills) and their panel entries moved out of the story
mod into the subsystem mods; the card_novel and vn_starter_novel collections
are removed.
This commit is contained in:
cthangover committed 2026-10-18 19:00:34 +02:00
1 parent c4e2130ed4
commit 22b67c628c
10 files changed
+26 -26

No files matched your search

+1 -1
View File
@@ -17,7 +17,7 @@
"locale": [
{ "lang": "ru", "name": "Главное меню", "description": "Главное меню, настройки и панели сохранения/загрузки." }
],
"mods": ["base_menu>=1", "interface>=1", "audio_base>=1"]
"mods": ["menu_base>=1", "ui_base>=1", "audio_base>=1"]
},
{
"id": "mods",
+2 -2
View File
@@ -1,5 +1,5 @@
mod_base_menu_name = Base Menu
mod_base_menu_desc = Registry-driven main menu: built-in buttons, settings and save/load panels moved out of the core.
mod_menu_base_name = Menu
mod_menu_base_desc = Registry-driven main menu: built-in buttons, settings and save/load panels moved out of the core.
settings/saves/purge_dormant = Data of disabled mechanics
settings/saves/purge_dormant_button = Purge
+2 -2
View File
@@ -1,5 +1,5 @@
mod_base_menu_name = Базовое меню
mod_base_menu_desc = Главное меню на реестрах: встроенные кнопки, настройки и панели сохранения/загрузки, вынесенные из ядра.
mod_menu_base_name = Меню
mod_menu_base_desc = Главное меню на реестрах: встроенные кнопки, настройки и панели сохранения/загрузки, вынесенные из ядра.
settings/saves/purge_dormant = Данные отключённых механик
settings/saves/purge_dormant_button = Очистить
+3 -3
View File
@@ -1,6 +1,6 @@
{
"id": "base_menu",
"name": "Base Menu",
"id": "menu_base",
"name": "Menu",
"version": "1.0.0",
"author": "ct",
"description": "Registry-driven main menu: built-in buttons, settings and save/load panels moved out of the core.",
@@ -8,7 +8,7 @@
"sources": [
"src/**/*.cs"
],
"depends": ["interface>=1"],
"depends": ["ui_base>=1"],
"test_suites": [
{"id": "menu_flow", "activate": ["--itest=menu_flow"]},
{"id": "save_dormancy", "activate": ["--itest=save_dormancy"]},
+1 -1
View File
@@ -17,7 +17,7 @@ namespace Cthangover.BaseMenu
/// </summary>
public class BaseMenuInitializer : IModInitializer
{
private const string ModId = "base_menu";
private const string ModId = "menu_base";
public void OnModLoaded(string modId)
{
@@ -189,7 +189,7 @@ namespace Mods.HotReloadTest
private static bool UnloadedAndLoadedBoth(ModSetReloadedEvent e) => UnloadedBoth(e) && LoadedBoth(e);
private static bool UnloadedBaseMenu(ModSetReloadedEvent e) => e.Unloaded.Contains("base_menu");
private static bool UnloadedBaseMenu(ModSetReloadedEvent e) => e.Unloaded.Contains("menu_base");
private static bool IsRejectedForLib(ModSetReloadedEvent e) => !e.Applied && e.Errors.Any(IsLibError);
@@ -23,7 +23,7 @@ namespace Mods.MenuFlowTest
/// </summary>
public partial class MenuFlowTestDriverNode : Node
{
private const string GameId = "card_novel";
private const string GameId = "novel_template";
private const string Slot = "slot_942";
private const string DeletableId = "menu_flow_deletable";
private const float TimeoutSeconds = 90f;
@@ -82,8 +82,8 @@ namespace Mods.MenuFlowTest
if (GetTree().CurrentScene is not MainMenu)
return;
Check("front-end only", ActiveGame.IsFrontendOnly && !ActiveGame.IsActive);
Check("menu mods loaded", ModRegistry.Instance.Mods.ContainsKey("base_menu") && ModRegistry.Instance.Mods.ContainsKey("mod_manager"));
Check("game content not loaded", !ModRegistry.Instance.Mods.ContainsKey("visual_novel_core"));
Check("menu mods loaded", ModRegistry.Instance.Mods.ContainsKey("menu_base") && ModRegistry.Instance.Mods.ContainsKey("mod_manager"));
Check("game content not loaded", !ModRegistry.Instance.Mods.ContainsKey("cthangover_story"));
Check("Play button shown", Menu.FindChild("play", true, false) is Button);
Check("New game hidden without a game", Menu.FindChild("new_game", true, false) == null);
break;
@@ -158,11 +158,11 @@ namespace Mods.MenuFlowTest
browser.Visible = false;
var composition = GameCatalog.Composition(GameId);
Check("composition computed without loading the game", composition?.Any(m => m.Id == "card_battle") == true);
Check("composition computed without loading the game", composition?.Any(m => m.Id == "novel_template") == true);
var diff = SaveCompositionDiff.Compute(
new[] { new SaveGameMod { Id = "visual_novel_core", Version = "2.0.0" }, new SaveGameMod { Id = "ghost_mechanic", Version = "1.0.0" } },
new[] { new SaveGameMod { Id = "cthangover_story", Version = "2.0.0" }, new SaveGameMod { Id = "ghost_mechanic", Version = "1.0.0" } },
composition);
Check("comparison reports removed and added mechanics", diff.Removed.Contains("ghost_mechanic") && diff.Added.Contains("card_battle"));
Check("comparison reports removed and added mechanics", diff.Removed.Contains("ghost_mechanic") && diff.Added.Contains("novel_template"));
LastSessionStore.Record(GameId, Slot);
Check("Continue sees the last session", LastSessionStore.Load()?.GameId == GameId);
@@ -191,7 +191,7 @@ namespace Mods.MenuFlowTest
SaveId = Slot,
Timestamp = DateTime.UtcNow,
CoreVersion = "0.1.0",
Game = new SaveGameInfo { Id = GameId, Name = "Card Novel", Version = "1.0.0" },
Game = new SaveGameInfo { Id = GameId, Name = "Novel Template", Version = "1.0.0" },
};
package.ModData[SavePackage.CoreSlotKey] = new ModSlot();
var dir = ProjectSettings.GlobalizePath(SaveService.GameDirectory(GameId));
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "New Game Flow Test",
"description": "End-to-end play like a player in one process: front-end menu → Play → the cthangover card → New game → in-process mod-set switch → the prologue in prolog_alley with Marao's dialog → in-game hot reload of starter_party (session restored) → menu → card_novel → menu → cthangover again; every released mod context must be collected. Activated only by --itest=new_game (with --frontend).",
"description": "End-to-end play like a player in one process: front-end menu → Play → the cthangover card → New game → in-process mod-set switch → the prologue in prolog_alley with Marao's dialog → in-game hot reload of cthangover_party (session restored) → menu → novel_template → menu → cthangover again; every released mod context must be collected. Activated only by --itest=new_game (with --frontend).",
"sources": [
"src/*.cs"
]
@@ -20,10 +20,10 @@ namespace Mods.NewGameTest
/// <item>Front-end menu → 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>starter_party</c>):
/// <item>Hot reload inside the game (forced reload of <c>cthangover_party</c>):
/// the session is parked, the mod reloads into a new context and the game
/// comes back in <c>prolog_alley</c>.</item>
/// <item>Back to the menu → <c>card_novel</c>: mods outside that game are
/// <item>Back to the menu → <c>novel_template</c>: mods outside that game are
/// unloaded and a scene of the game is entered.</item>
/// <item>Back to the menu → <c>cthangover</c> again → <c>prolog_alley</c>.</item>
/// </list>
@@ -38,9 +38,9 @@ namespace Mods.NewGameTest
public const string Flag = "--itest=new_game";
private const string MainGame = "cthangover";
private const string SecondGame = "card_novel";
private const string SecondGame = "novel_template";
private const string PrologScene = "prolog_alley";
private const string HotReloadedMod = "starter_party";
private const string HotReloadedMod = "cthangover_party";
private const float TimeoutSeconds = 420f;
private readonly List<string> failures = new();
@@ -160,7 +160,7 @@ namespace Mods.NewGameTest
var reload = ModManager.Instance.LastReload;
Check("in-game hot reload applied", reload?.Applied == true);
Check($"hot reload reloaded '{HotReloadedMod}'", reload?.Plan?.Load.Contains(HotReloadedMod) == true);
Check("hot reload kept the front-end loaded", reload?.Plan?.Keep.Contains("base_menu") == true);
Check("hot reload kept the front-end loaded", reload?.Plan?.Keep.Contains("menu_base") == true);
Check("hot reload released every old context", reload?.Leaked.Count == 0);
Check($"hot reload kept the game '{MainGame}'", ActiveGame.Id == MainGame);
Check($"session came back in '{PrologScene}'", true);
@@ -255,7 +255,7 @@ namespace Mods.NewGameTest
{
var reload = ModManager.Instance.LastReload;
Check($"switch into '{gameId}' applied", reload?.Applied == true);
Check($"switch into '{gameId}' kept the front-end loaded", reload?.Plan?.Keep.Contains("base_menu") == true);
Check($"switch into '{gameId}' kept the front-end loaded", reload?.Plan?.Keep.Contains("menu_base") == true);
if (expectUnload)
Check($"switch into '{gameId}' unloaded mods outside the game", reload?.Plan?.Unload.Count > 0);
Check($"switch into '{gameId}' released every old context", reload?.Leaked.Count == 0);
@@ -76,7 +76,7 @@ namespace Mods.SaveDormancyTest
var absentSlot = new ModSlot { Metadata = new ModMetadata { ModId = AbsentMod, ModVersion = "1.1.0" } };
absentSlot.Data["marker"] = new DataBlob(1, new Dictionary<string, string> { ["value"] = "42" });
package.ModData[AbsentMod] = absentSlot;
package.ModData.Remove("base_menu__save_dormancy");
package.ModData.Remove("menu_base__save_dormancy");
File.WriteAllText(pathA, JsonSerializer.Serialize(package, JsonOptionsProvider.WriteCompactOptions));
SaveDormancyProbe.Joined = false;
@@ -93,7 +93,7 @@ namespace Mods.SaveDormancyTest
&& marker.ReadJson<Dictionary<string, string>>().GetValueOrDefault("value") == "42");
Check("live mods still saved", resaved?.ModData?.ContainsKey(SavePackage.CoreSlotKey) == true);
var message = ModSettingsRegistry.RunAction("base_menu", "purge_dormant");
var message = ModSettingsRegistry.RunAction("menu_base", "purge_dormant");
Check("purge action runs from the save settings", !string.IsNullOrEmpty(message));
Check("purge removed dormant data from the save", Read(FilePath(SlotB))?.Dormant == null);