diff --git a/CLAUDE.md b/CLAUDE.md index 42d56133..66c9610a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -104,7 +104,7 @@ wow_process.txt 目标游戏进程名列表;构建时复制,运行期间每 - [UI/ClassConfigEditorControl.cs](UI/ClassConfigEditorControl.cs):左侧职业列表 + 右侧按专精切换的六页编辑器(状态/光环/冷却/队伍/技能列表/物品列表),状态字段用 `ClassStateCatalog` 驱动的 `ComboBoxColumn`。 - [UI/ClassMacrosEditorControl.cs](UI/ClassMacrosEditorControl.cs):左侧职业列表 + 右侧三页编辑器(动态宏/静态宏/特殊宏),偏移提示显示槽位编号计算。 -- 两个编辑器均接受 `Func` 项目路径解析器 + `Func>` 保存回调,由 `MainForm` 在构造时注入。保存流程:编辑器调 `Store.Save()` → 传入已保存文件路径 → 重新生成 config/keymap → 单文件部署游戏 → 重启运行时;部署失败返回说明,但不回滚本地文件。 +- 两个编辑器均接受 `Func` 项目路径解析器 + `Func>` 保存回调,由 `MainForm` 在构造时注入。保存流程:编辑器调 `Store.Save()` → 同步保存该职业模块依赖快照 → 传入已保存文件路径 → 重新生成 config/keymap → 单文件部署游戏 → 重启运行时;部署失败返回说明,但不回滚本地文件。 - 配置更新的多个入口通过任务尾队列串行执行;运行时重启会等待该队列稳定,主窗口关闭也会等待正在写盘的转换和部署完成。新增同步入口必须继续走这条队列。 ## UI 约定 diff --git a/UI/ClassMacrosEditorControl.cs b/UI/ClassMacrosEditorControl.cs index 7be30f5d..1b251b45 100644 --- a/UI/ClassMacrosEditorControl.cs +++ b/UI/ClassMacrosEditorControl.cs @@ -10,7 +10,7 @@ namespace Shigure; public sealed class ClassMacrosEditorControl : UserControl { private readonly Func _resolveClassMacrosPath; - private readonly Func> _updateConfigAsync; + private readonly Func> _updateConfigAsync; private readonly ListBox _classList = new(); private readonly Label _pathLabel = new(); @@ -43,7 +43,7 @@ public sealed class ClassMacrosEditorControl : UserControl public ClassMacrosEditorControl( Func resolveClassMacrosPath, - Func> updateConfigAsync) + Func> updateConfigAsync) { _resolveClassMacrosPath = resolveClassMacrosPath; _updateConfigAsync = updateConfigAsync; @@ -1020,6 +1020,12 @@ public sealed class ClassMacrosEditorControl : UserControl return; } + if (_currentClassId is not { } classId) + { + MessageBox.Show("请先选择职业。", "宏", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + var localSaved = false; try { @@ -1028,17 +1034,18 @@ public sealed class ClassMacrosEditorControl : UserControl localSaved = true; SetDirty(false); _statusLabel.Text = "本地 Lua 已保存,正在更新配置并同步游戏…"; - var syncIssue = await _updateConfigAsync(_document.FilePath); + var updateResult = await _updateConfigAsync(_document.FilePath, classId); if (IsDisposed) { return; } - if (!string.IsNullOrWhiteSpace(syncIssue)) + if (!string.IsNullOrWhiteSpace(updateResult.AddonSyncIssue)) { _statusLabel.Text = "本地已保存并更新配置,但游戏同步失败"; MessageBox.Show( - $"本地 Lua 已保存,config/keymap 已更新,但游戏插件同步未完成:\n{syncIssue}", + $"本地 Lua、该职业的 {updateResult.SavedModuleCount} 个模块及 config/keymap 已保存," + + $"但游戏插件同步未完成:\n{updateResult.AddonSyncIssue}", "游戏同步失败", MessageBoxButtons.OK, MessageBoxIcon.Warning); @@ -1046,13 +1053,16 @@ public sealed class ClassMacrosEditorControl : UserControl return; } - _statusLabel.Text = "已保存并更新配置"; + _statusLabel.Text = $"已保存宏及该职业的 {updateResult.SavedModuleCount} 个模块"; UpdateOffsetHint(); + var warningText = updateResult.ModuleWarnings.Count == 0 + ? string.Empty + : $"\n\n有 {updateResult.ModuleWarnings.Count} 个模块未携带依赖,详情见日志。"; MessageBox.Show( - "请在游戏内重载界面, /reload", + $"已一并保存该职业的 {updateResult.SavedModuleCount} 个模块。{warningText}\n\n请在游戏内重载界面, /reload", "保存成功", MessageBoxButtons.OK, - MessageBoxIcon.Information); + updateResult.ModuleWarnings.Count == 0 ? MessageBoxIcon.Information : MessageBoxIcon.Warning); } catch (Exception ex) { diff --git a/UI/MainForm.cs b/UI/MainForm.cs index db631ca2..a19cbce8 100644 --- a/UI/MainForm.cs +++ b/UI/MainForm.cs @@ -185,7 +185,7 @@ public sealed class MainForm : Form, IMessageFilter _classConfigEditor.DirtyStateChanged += dirty => _statusForm.SetPageDirty(SettingsPage.Config, dirty); _classMacrosEditor = new ClassMacrosEditorControl( () => Path.Combine(_addonSyncService.SourceRoot, "core", "classmacros.lua"), - UpdateConfigAfterSaveAsync); + UpdateClassConfigAfterSaveAsync); _statusForm.AttachMacrosEditor(_classMacrosEditor); _classMacrosEditor.DirtyStateChanged += dirty => _statusForm.SetPageDirty(SettingsPage.Macros, dirty); _statusForm.FormClosing += (_, _) => @@ -1497,12 +1497,6 @@ public sealed class MainForm : Form, IMessageFilter } } - private async Task UpdateConfigAfterSaveAsync(string savedAddonFilePath) - { - var result = await QueueProjectConfigUpdateAsync(savedAddonFilePath); - return DescribeAddonSyncIssue(result.AddonSync); - } - private async Task UpdateClassConfigAfterSaveAsync( string savedAddonFilePath, int classId) @@ -1512,7 +1506,7 @@ public sealed class MainForm : Form, IMessageFilter if (moduleResult.Errors.Count > 0) { throw new InvalidOperationException( - $"配置已更新,但该职业有 {moduleResult.Errors.Count} 个模块保存失败;" + $"该职业有 {moduleResult.Errors.Count} 个模块保存失败;" + $"已成功保存 {moduleResult.SavedCount} 个模块。详情见日志。"); } @@ -1551,7 +1545,7 @@ public sealed class MainForm : Form, IMessageFilter } var className = ClassNames.GetClassAndSpecName(classId, null).ClassName ?? $"职业{classId}"; - AppendLog($"保存{className}配置时已同步保存 {savedCount}/{modules.Count} 个该职业模块。"); + AppendLog($"已同步保存 {savedCount}/{modules.Count} 个{className}模块。"); foreach (var warning in warnings) { AppendLog($"模块依赖保存提示: {warning}");