diff --git a/CLAUDE.md b/CLAUDE.md index 66c9610a..5b2247c8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -98,7 +98,7 @@ wow_process.txt 目标游戏进程名列表;构建时复制,运行期间每 - **DynamicSpells**:每项占 30 热键槽位 - **StaticSpells / SpecialSpells**:顺序数组条目(`ArrayEntry`:text + 可选行尾注释);空字符串保留槽位 -保存后 [Infrastructure/FuyutsuiKeymapConverter.cs](Infrastructure/FuyutsuiKeymapConverter.cs)`.UpdateFromClassMacros` 将 Lua 宏表按 dynamic(每项 30 槽)→ static → special 顺序转换为 `keymap/*.json`,把宏槽位映射到热键池(7 修饰符 × 39 键 = 273 组合/职业)。`DeriveSpellName` 解析 WoW 宏文本提取技能名。 +保存后 [Infrastructure/FuyutsuiKeymapConverter.cs](Infrastructure/FuyutsuiKeymapConverter.cs)`.UpdateFromClassMacros` 将 Lua 宏表按 dynamic(每项 30 槽)→ static → special 顺序转换为 `keymap/*.json`,把宏槽位映射到热键池(先 7 修饰符 × 39 键 = 273,再追加 7 × 11 个导航/减号/方向键 = 350 组合/职业;不含 F4)。`DeriveSpellName` 解析 WoW 宏文本提取技能名。 ### UI 编辑器 diff --git a/Fuyutsui/core/config.lua b/Fuyutsui/core/config.lua index 93baeedf..dc3fab5e 100644 --- a/Fuyutsui/core/config.lua +++ b/Fuyutsui/core/config.lua @@ -393,6 +393,16 @@ Fuyutsui.keymap = { ["."] = 190, ["PERIOD"] = 190, -- WoW可能返回PERIOD而不是. ["/"] = 191, + ["INSERT"] = 45, + ["DELETE"] = 46, + ["HOME"] = 36, + ["END"] = 35, + ["PAGEUP"] = 33, + ["PAGEDOWN"] = 34, + ["LEFT"] = 37, + ["UP"] = 38, + ["RIGHT"] = 39, + ["DOWN"] = 40, } -- 角色类型映射 Fuyutsui.roleMap = { diff --git a/Fuyutsui/core/macro.lua b/Fuyutsui/core/macro.lua index 99484cea..4ba70aca 100644 --- a/Fuyutsui/core/macro.lua +++ b/Fuyutsui/core/macro.lua @@ -18,6 +18,13 @@ local keys = { "7", "8", "9", "0", "=" } +-- 接在原 273 槽之后,避免把新主键插入 keys 导致后续修饰组错位。 +-- 不含 F4(ALT-F4 关客户端)、反引号、NUMPADENTER。 +local extraKeys = { + "-", "INSERT", "DELETE", "HOME", "END", "PAGEUP", "PAGEDOWN", + "UP", "DOWN", "LEFT", "RIGHT" +} + do local i = 1 for _, m in ipairs(modifiers) do @@ -26,6 +33,12 @@ do i = i + 1 end end + for _, m in ipairs(modifiers) do + for _, k in ipairs(extraKeys) do + macroKind[i] = m .. "-" .. k + i = i + 1 + end + end end diff --git a/Infrastructure/FuyutsuiKeymapConverter.cs b/Infrastructure/FuyutsuiKeymapConverter.cs index 04f2f8d1..ee31a46e 100644 --- a/Infrastructure/FuyutsuiKeymapConverter.cs +++ b/Infrastructure/FuyutsuiKeymapConverter.cs @@ -35,9 +35,16 @@ internal static partial class FuyutsuiKeymapConverter "7", "8", "9", "0", "=" ]; + // 接在原 273 槽之后;不含 F4、反引号、NUMPADENTER。 + private static readonly string[] ExtraKeys = + [ + "-", "INSERT", "DELETE", "HOME", "END", "PAGEUP", "PAGEDOWN", + "UP", "DOWN", "LEFT", "RIGHT" + ]; + private static readonly string[] MacroKind = BuildMacroKind(); - internal static int MacroSlotCapacity => Modifiers.Length * Keys.Length; + internal static int MacroSlotCapacity => Modifiers.Length * (Keys.Length + ExtraKeys.Length); private static readonly Dictionary ClassFileToId = new(StringComparer.OrdinalIgnoreCase) { @@ -637,7 +644,7 @@ internal static partial class FuyutsuiKeymapConverter private static string[] BuildMacroKind() { - var list = new string[Modifiers.Length * Keys.Length]; + var list = new string[MacroSlotCapacity]; var i = 0; foreach (var modifier in Modifiers) { @@ -647,6 +654,14 @@ internal static partial class FuyutsuiKeymapConverter } } + foreach (var modifier in Modifiers) + { + foreach (var key in ExtraKeys) + { + list[i++] = $"{modifier}-{key}"; + } + } + return list; } diff --git a/Input/KeySender.cs b/Input/KeySender.cs index 9eb1b4cb..5f041d4d 100644 --- a/Input/KeySender.cs +++ b/Input/KeySender.cs @@ -54,9 +54,10 @@ public sealed class KeySender : IRuntimeKeyOutput var succeeded = true; var firstError = 0; - void SendMessage(int vk, bool keyUp) + var mainExtended = IsExtendedKey(mainKey); + void SendMessage(int vk, bool keyUp, bool extended = false) { - if (!Post(hwnd, vk, keyUp, out var error)) + if (!Post(hwnd, vk, keyUp, extended, out var error)) { succeeded = false; if (firstError == 0) @@ -71,8 +72,8 @@ public sealed class KeySender : IRuntimeKeyOutput SendMessage(vk, keyUp: false); } - SendMessage(vkMain.Value, keyUp: false); - SendMessage(vkMain.Value, keyUp: true); + SendMessage(vkMain.Value, keyUp: false, mainExtended); + SendMessage(vkMain.Value, keyUp: true, mainExtended); for (var i = modVks.Count - 1; i >= 0; i--) { @@ -96,40 +97,84 @@ public sealed class KeySender : IRuntimeKeyOutput return (new List(), null); } - var rawParts = hotkey.Trim().Split('-', StringSplitOptions.RemoveEmptyEntries); - if (rawParts.Length == 0) - { - return (new List(), null); - } - - var mainKey = rawParts[^1]; + // 从左消费修饰前缀,剩余整段当主键。CTRL-- 不能按 '-' 切开,否则会丢掉减号。 + var remaining = hotkey.Trim(); var mods = new List(); - foreach (var raw in rawParts[..^1]) + while (TryConsumeModifierPrefix(ref remaining, out var modifier)) { - var part = raw.Trim().ToUpperInvariant(); - part = part switch + if (!mods.Contains(modifier)) { - "CONTROL" => "CTRL", - "MENU" => "ALT", - _ => part - }; - - if (part is "CTRL" or "ALT" or "SHIFT" && !mods.Contains(part)) - { - mods.Add(part); + mods.Add(modifier); } } - return (mods, mainKey); + return remaining.Length == 0 ? (mods, null) : (mods, remaining); } + private static bool TryConsumeModifierPrefix(ref string remaining, out string modifier) + { + if (StartsWithIgnoreCase(remaining, "CONTROL-")) + { + remaining = remaining["CONTROL-".Length..]; + modifier = "CTRL"; + return true; + } + + if (StartsWithIgnoreCase(remaining, "CTRL-")) + { + remaining = remaining["CTRL-".Length..]; + modifier = "CTRL"; + return true; + } + + if (StartsWithIgnoreCase(remaining, "MENU-")) + { + remaining = remaining["MENU-".Length..]; + modifier = "ALT"; + return true; + } + + if (StartsWithIgnoreCase(remaining, "ALT-")) + { + remaining = remaining["ALT-".Length..]; + modifier = "ALT"; + return true; + } + + if (StartsWithIgnoreCase(remaining, "SHIFT-")) + { + remaining = remaining["SHIFT-".Length..]; + modifier = "SHIFT"; + return true; + } + + modifier = ""; + return false; + } + + private static bool StartsWithIgnoreCase(string value, string prefix) + => value.StartsWith(prefix, StringComparison.OrdinalIgnoreCase); + + private static bool IsExtendedKey(string keyName) + => keyName.Equals("NUMPADDIVIDE", StringComparison.OrdinalIgnoreCase) + || keyName.Equals("INSERT", StringComparison.OrdinalIgnoreCase) + || keyName.Equals("DELETE", StringComparison.OrdinalIgnoreCase) + || keyName.Equals("HOME", StringComparison.OrdinalIgnoreCase) + || keyName.Equals("END", StringComparison.OrdinalIgnoreCase) + || keyName.Equals("PAGEUP", StringComparison.OrdinalIgnoreCase) + || keyName.Equals("PAGEDOWN", StringComparison.OrdinalIgnoreCase) + || keyName.Equals("LEFT", StringComparison.OrdinalIgnoreCase) + || keyName.Equals("UP", StringComparison.OrdinalIgnoreCase) + || keyName.Equals("RIGHT", StringComparison.OrdinalIgnoreCase) + || keyName.Equals("DOWN", StringComparison.OrdinalIgnoreCase); + private static KeySendResult Fail(string reason) => KeySendResult.Failure(reason); - private static bool Post(nint hwnd, int keyCode, bool keyUp, out int error) + private static bool Post(nint hwnd, int keyCode, bool keyUp, bool extended, out int error) { var scanCode = NativeMethods.MapVirtualKeyW((uint)keyCode, 0) & 0xFF; var value = 1u | (scanCode << 16); - if (keyCode == 0x6F) // VK_DIVIDE 是扩展键。 + if (extended || keyCode == 0x6F) // VK_DIVIDE 与导航/方向键是扩展键。 { value |= 1u << 24; } diff --git a/Input/WindowsVirtualKeyMap.cs b/Input/WindowsVirtualKeyMap.cs index 6291ee55..83c52529 100644 --- a/Input/WindowsVirtualKeyMap.cs +++ b/Input/WindowsVirtualKeyMap.cs @@ -41,7 +41,17 @@ internal static class WindowsVirtualKeyMap ["NUMPADPLUS"] = 0x6B, ["NUMPADMINUS"] = 0x6D, ["NUMPADMULTIPLY"] = 0x6A, - ["NUMPADDIVIDE"] = 0x6F + ["NUMPADDIVIDE"] = 0x6F, + ["INSERT"] = 0x2D, + ["DELETE"] = 0x2E, + ["HOME"] = 0x24, + ["END"] = 0x23, + ["PAGEUP"] = 0x21, + ["PAGEDOWN"] = 0x22, + ["LEFT"] = 0x25, + ["UP"] = 0x26, + ["RIGHT"] = 0x27, + ["DOWN"] = 0x28 }; private static readonly Dictionary CharacterKeys = new() diff --git a/Obsidian/20-Fuyutsui/09-Fuyutsui-动作条键位扫描.md b/Obsidian/20-Fuyutsui/09-Fuyutsui-动作条键位扫描.md index cfae64bb..de1d7ec0 100644 --- a/Obsidian/20-Fuyutsui/09-Fuyutsui-动作条键位扫描.md +++ b/Obsidian/20-Fuyutsui/09-Fuyutsui-动作条键位扫描.md @@ -43,7 +43,7 @@ verified_at: 2026-08-09 ## AI 快速摘要 -> `ReadKeybindings()` 延迟 0.5 秒扫描动作槽 1..180,把可识别的 macro/spell 记录为 `Fuyutsui.keybindings[spellID] = {key, slot, keycode, icon, name}`。这是游戏内诊断缓存;Shigure 的实际发送映射来自 `ClassMacros` 转换出的 keymap,而不是读取该 Lua 表。插件自己的安全宏按钮使用 7 种修饰键×39 个基础键,共 273 个顺序槽。 +> `ReadKeybindings()` 延迟 0.5 秒扫描动作槽 1..180,把可识别的 macro/spell 记录为 `Fuyutsui.keybindings[spellID] = {key, slot, keycode, icon, name}`。这是游戏内诊断缓存;Shigure 的实际发送映射来自 `ClassMacros` 转换出的 keymap,而不是读取该 Lua 表。插件自己的安全宏按钮先按 7 种修饰键×39 个基础键生成 273 槽,再追加 7×11 个导航/减号/方向键,共 350 个顺序槽。 ## 范围与非范围 @@ -93,18 +93,19 @@ OnEnable / UPDATE_BINDINGS / SPELLS_CHANGED / ACTIONBAR_*GRID ## 安全宏键池 -`core/macro.lua:6-29` 生成固定键池: +`core/macro.lua` 分两段生成固定键池: - 修饰键顺序:`CTRL`、`ALT`、`SHIFT`、`ALT-CTRL`、`ALT-SHIFT`、`CTRL-SHIFT`、`ALT-CTRL-SHIFT`。 -- 每组基础键 39 个,包括小键盘、F 键、标点和部分数字键。 -- 总容量 `7 × 39 = 273`。 +- 第一段基础键 39 个,包括小键盘、F 键(无 F4)、标点和部分数字键,形成槽 1–273。 +- 第二段在末尾追加 `-`、`INSERT`、`DELETE`、`HOME`、`END`、`PAGEUP`、`PAGEDOWN`、`UP`、`DOWN`、`LEFT`、`RIGHT`,再乘 7 种修饰符,形成槽 274–350。不加反引号、`NUMPADENTER`。 +- 总容量 `273 + 7 × 11 = 350`。 - 第 `i` 槽创建名为 `s{i}` 的 `SecureActionButtonTemplate`,通过 `SetOverrideBindingClick()` 绑定对应按键。 宏体解析顺序:先查 `Fuyutsui.MacroBodies` 的命名宏体;字符串以 `/` 开头则原样使用;否则包装为 `/cast <字符串>`。 ## ClassMacros 到按钮的确定顺序 -`LoadPlayerMacros()` 合并当前职业的 common + 当前专精 dynamic 列表后,`CreateMacro()` 按实际源码顺序消费 273 个槽: +`LoadPlayerMacros()` 合并当前职业的 common + 当前专精 dynamic 列表后,`CreateMacro()` 按实际源码顺序消费最多 350 个槽: 1. `dynamicSpells`:每个定义固定占 30 槽,展开 raid1..raid30,并兼容 party/player 条件。 2. `staticSpells`:每项占 1 槽;空字符串只保留占位,不创建按钮。 @@ -118,7 +119,7 @@ OnEnable / UPDATE_BINDINGS / SPELLS_CHANGED / ACTIONBAR_*GRID - Lua `macroKind[i]` 与 C# 生成 keymap 的第 `i` 项必须逐项相同,包括修饰键文本顺序。 - 每个 dynamic 条目始终占 30 槽,即使某些宏体为空。 - static/special 空项同样占位;删除占位会让后续所有键左移。 -- 总展开数不得超过 273;超出部分 `nextSlot()` 静默不创建。 +- 总展开数不得超过 350;超出部分 `nextSlot()` 静默不创建。 - 游戏内动作条 `Fuyutsui.keybindings` 不是跨进程契约源,不能拿它替换生成 keymap。 ## 失败模式与当前风险 @@ -133,7 +134,7 @@ OnEnable / UPDATE_BINDINGS / SPELLS_CHANGED / ACTIONBAR_*GRID ## 修改影响 -- 修改 `modifiers`、`keys` 或三段展开顺序时,必须同步 [[40-跨项目/03-Shigure-ClassMacros到keymap与按键契约]] 与 Shigure 转换器。 +- 修改 `modifiers`、`keys`、`extraKeys` 或三段展开顺序时,必须同步 [[40-跨项目/03-Shigure-ClassMacros到keymap与按键契约]] 与 Shigure 转换器。 - 修改 `ClassMacros` 后使用 Shigure 的配置/宏同步流程,见 [[30-Shigure/09-Shigure-Fuyutsui配置宏编辑与同步]]。 - 修复动作条宏识别时应按 WoW 当前 API 先解析宏信息/宏体或宏所示技能,再调用 Spell API。 - 若要让游戏内缓存成为外部契约,需新增明确序列化与版本字段;当前没有这条链路。 @@ -143,7 +144,7 @@ OnEnable / UPDATE_BINDINGS / SPELLS_CHANGED / ACTIONBAR_*GRID - `Fuyutsui/core/config.lua:228-345`:动作条范围与 keycode 表。 - `Fuyutsui/core/keybinds.lua:1-53`:1..180 扫描和本地缓存。 - `Fuyutsui/core/events.lua:314-328`:重新扫描触发事件。 -- `Fuyutsui/core/macro.lua:1-126`:273 键池、安全按钮和三段展开顺序。 +- `Fuyutsui/core/macro.lua`:350 键池、安全按钮和三段展开顺序。 - `Fuyutsui/core/classmacros.lua:1-7`:命名宏体。 - `Fuyutsui/main.lua:197-236`:当前专精 dynamic 合并与宏创建入口。 diff --git a/Obsidian/30-Shigure/08-Shigure-Keymap解析与按键发送.md b/Obsidian/30-Shigure/08-Shigure-Keymap解析与按键发送.md index 0d239ca4..0e53ab24 100644 --- a/Obsidian/30-Shigure/08-Shigure-Keymap解析与按键发送.md +++ b/Obsidian/30-Shigure/08-Shigure-Keymap解析与按键发送.md @@ -50,7 +50,7 @@ verified_at: 2026-08-10 ## 范围与非范围 -本页覆盖运行时 Keymap 选择和 Windows 输出。Lua 宏如何转换为 273 个键位项见 [[30-Shigure/09-Shigure-Fuyutsui配置宏编辑与同步]];触发模式状态机见运行循环页。 +本页覆盖运行时 Keymap 选择和 Windows 输出。Lua 宏如何转换为 350 个键位项见 [[30-Shigure/09-Shigure-Fuyutsui配置宏编辑与同步]];触发模式状态机见运行循环页。 ## 从规则到按键 @@ -88,9 +88,9 @@ ModuleRule(Unit / Spell / MacroCondition) ## 虚拟键解析 -- 命名键包含 CTRL/ALT/SHIFT、鼠标侧键、F1..F12、数字小键盘和常见标点。 +- 命名键包含 CTRL/ALT/SHIFT、鼠标侧键、F1..F12、数字小键盘、导航键(INSERT/DELETE/HOME/END/PAGEUP/PAGEDOWN)、方向键和常见标点(含 `-`)。 - 单字符可通过 `VkKeyScan` 转为主键及隐含修饰键。 -- chord 中只有 CTRL、ALT、SHIFT 被保留为修饰键;未知的修饰 token 会被忽略,最后解析到的普通键作为主键。 +- chord 从左消费 CTRL/ALT/SHIFT 前缀,剩余整段当主键;因此 `CTRL--` 的主键是 `-`,不能按 `-` 切开。 - UI 不允许把 ALT 设为**触发键**,并会把已有 ALT 触发配置回退到 XBUTTON2;这不影响把 ALT 用作**输出 Hotkey 修饰键**。 ## Windows 消息输出边界 @@ -99,7 +99,7 @@ ModuleRule(Unit / Spell / MacroCondition) 2. 运行时传入本轮扫描得到的窗口句柄;若发送时目标已切换,放弃发送并等待重新扫描。 3. 依次投递修饰键按下、主键按下、主键抬起、修饰键逆序抬起。 4. 使用 `WM_KEYDOWN` / `WM_KEYUP`,不会主动激活或切到前台。 -5. 目前只有数字小键盘除号设置 extended-key 标志;发送错误 5 通常指向完整性级别/UIPI 权限差异。 +5. 小键盘除号、导航六键和四个方向键设置 extended-key 标志;发送错误 5 通常指向完整性级别/UIPI 权限差异。 目标先按配置进程名限定 PID,再按 Z 顺序选择窗口;它不验证签名或固定某个进程实例。多个候选实例之间切换会触发句柄防陈旧检查;目标程序是否处理后台 `PostMessage` 仍取决于其消息循环和权限。 diff --git a/Obsidian/30-Shigure/09-Shigure-Fuyutsui配置宏编辑与同步.md b/Obsidian/30-Shigure/09-Shigure-Fuyutsui配置宏编辑与同步.md index 53d43432..aabadcdc 100644 --- a/Obsidian/30-Shigure/09-Shigure-Fuyutsui配置宏编辑与同步.md +++ b/Obsidian/30-Shigure/09-Shigure-Fuyutsui配置宏编辑与同步.md @@ -117,14 +117,14 @@ verified_at: 2026-08-10 ## ClassMacros → keymap -- 固定键池为 7 个修饰组合 × 39 个主键 = **273** 项;主键序列刻意不含 F4。顺序必须与 Fuyutsui 动作条扫描完全一致。 +- 固定键池先按 7 个修饰组合 × 39 个主键生成 **273** 项(主键序列刻意不含 F4),再在末尾追加 7 × 11 项(`-`、导航六键、四个方向键),共 **350** 项。不含反引号和 `NUMPADENTER`。Lua `macroKind` 与 C# `FuyutsuiKeymapConverter` 必须逐项相同。 - 动态宏项每项预留/消耗 30 个单位位置,然后是静态宏和特殊宏;超过容量会警告并截断。 - 旧 flat 格式或 common+spec 格式都可转换;专精输出是“common + 当前 spec + static + special”的完整映射,以适配运行时不合并顶层的语义。 - party1..4 映射组员槽 2..5;原始 `@player` 动态语义映射组员槽 1;显式中文/player 保留单位 31;raid1..30 映射 1..30。 - dynamic/static 仍按宏文本生成映射,方括号中非 `@` 条件汇总为 macroCondition,尾注释可以覆盖推导出的 spell 名。 - special 不再分析宏正文:尾注释必须作为手工技能名,映射固定为 `unit=0`、空 macroCondition;宏编辑器因此只显示可编辑技能名和完整宏,不显示目标与条件。 - 能识别 stopcasting、castsequence、最后一个 cast 和 item 等受支持形式;不等于完整 WoW 宏解释器。 -- 输出包含全部 273 项,包括空项,以维持索引位置。 +- 输出包含全部 350 项,包括空项,以维持索引位置。 ## 同步队列和运行时重启 @@ -146,7 +146,7 @@ verified_at: 2026-08-10 | 旧 ClassBlocks 页面为空 | legacy sparse 专精不被完整建模;不要保存混合文件 | | config 部分职业是新、部分是旧 | 13 文件转换非事务,某个文件失败后可能已有部分覆盖 | | 某状态永远为 0 | 步骤顺序、重复名覆盖、是否超过 510 | -| Keymap 后半被截断 | 动态项每项消耗 30,整体超过 273 | +| Keymap 后半被截断 | 动态项每项消耗 30,整体超过 350 | | 刚保存但运行时仍用旧数据 | 等待同步队列结束并确认会话已重启 | | 游戏目录手工修改消失 | 游戏副本不是权威源;下次全量或同文件部署会以项目源覆盖 | @@ -167,7 +167,7 @@ verified_at: 2026-08-10 - `Infrastructure/ClassBlocksStore.cs:102-207`:modern 判定、替换保存与 legacy 行为。 - `Infrastructure/ClassMacrosStore.cs:63-354`:宏表加载、直接写回和 canonical 序列化。 - `Infrastructure/FuyutsuiConfigConverter.cs:44-483`:13 职业、步骤顺序、组员与字段规范化。 -- `Infrastructure/FuyutsuiKeymapConverter.cs:21-475`:273 键池、单位映射、宏/注释解析。 +- `Infrastructure/FuyutsuiKeymapConverter.cs`:350 键池(前 273 不变)、单位映射、宏/注释解析。 - `UI/MainForm.cs:624-867`:启动部署、串行更新尾、转换、全量/单文件部署、刷新和告警。 - `UI/MainForm.cs:781-958`:运行前等待队列并重启会话。 diff --git a/Obsidian/30-Shigure/11-Shigure-本地数据路径构建与验证.md b/Obsidian/30-Shigure/11-Shigure-本地数据路径构建与验证.md index 770bbdb5..6de37ced 100644 --- a/Obsidian/30-Shigure/11-Shigure-本地数据路径构建与验证.md +++ b/Obsidian/30-Shigure/11-Shigure-本地数据路径构建与验证.md @@ -107,13 +107,13 @@ verified_at: 2026-08-10 1. **静态核对**:只检查源码、项目文件和受版本控制的数据,排除 `bin/obj`。 2. **构建**:在已安装 .NET 10 SDK 的 Windows 环境,从仓库根运行 `dotnet build .\Shigure.slnx`。 -3. **数据健全性**:确认 common + 13 职业 JSON 齐全、模块 JSON 可加载、Keymap 不超过/完整覆盖 273 位置、ClassBlocks 最终步骤不超过 510。 +3. **数据健全性**:确认 common + 13 职业 JSON 齐全、模块 JSON 可加载、Keymap 不超过/完整覆盖 350 位置、ClassBlocks 最终步骤不超过 510。 4. **部署验证**:发布目录含完整 `Fuyutsui/`;打开目标游戏后启动 Shigure,核对缺失/不同文件被复制、相同文件跳过、额外文件保留。 5. **端到端扫描**:让 WoW 重载已部署的 Fuyutsui,检查步骤 1、职业/专精、计数条和六行吸收。 6. **决策/输出**:用无风险场景检查模块匹配、规则短路、三触发模式、延迟以及目标窗口消息权限。 7. **round-trip 备份验证**:对项目 Lua 编辑前先备份,比较保存前后目标 table、生成 JSON 和游戏部署副本。 -没有测试项目意味着“构建成功”不能证明跨项目协议正确;像素颜色、步骤顺序、273 键池和单位编号都需要生产者/消费者联合验证。 +没有测试项目意味着“构建成功”不能证明跨项目协议正确;像素颜色、步骤顺序、350 键池和单位编号都需要生产者/消费者联合验证。 ## 安全与信任边界 diff --git a/Obsidian/40-跨项目/03-Shigure-ClassMacros到keymap与按键契约.md b/Obsidian/40-跨项目/03-Shigure-ClassMacros到keymap与按键契约.md index 0378b7c4..956e0179 100644 --- a/Obsidian/40-跨项目/03-Shigure-ClassMacros到keymap与按键契约.md +++ b/Obsidian/40-跨项目/03-Shigure-ClassMacros到keymap与按键契约.md @@ -56,7 +56,7 @@ dynamicSpells(每项展开 30 个团队槽) → specialSpells ``` -`dynamicSpells` 支持 `common + [specIndex]`;Fuyutsui 运行时和 Shigure 转换器必须按相同专精解析。宏键池容量由 Fuyutsui 预设组合决定,当前参考实现按 7 组修饰符 × 39 个键形成 273 个组合,新增内容必须先做容量预算。 +`dynamicSpells` 支持 `common + [specIndex]`;Fuyutsui 运行时和 Shigure 转换器必须按相同专精解析。宏键池容量由 Fuyutsui 预设组合决定:先 7 组修饰符 × 39 个原主键(273),再追加 7 × 11 个主键(减号、导航六键、四个方向键)共 350 个组合。不含 F4、反引号、NUMPADENTER。新增内容必须先做容量预算,且只能往键池末尾追加,不能插入原 39 键中间。 ## 范围 diff --git a/Obsidian/50-参考资料/CLASSMACROS_AI_Reference_zh-CN.md b/Obsidian/50-参考资料/CLASSMACROS_AI_Reference_zh-CN.md index 170ce96c..120ec7e5 100644 --- a/Obsidian/50-参考资料/CLASSMACROS_AI_Reference_zh-CN.md +++ b/Obsidian/50-参考资料/CLASSMACROS_AI_Reference_zh-CN.md @@ -1,6 +1,6 @@ --- title: "Fuyutsui core/classmacros.lua:AI 宏规则参考" -summary: "说明 ClassMacros 的动态、静态、特殊宏声明,以及 CreateMacro 的 273 槽展开与覆盖绑定顺序。" +summary: "说明 ClassMacros 的动态、静态、特殊宏声明,以及 CreateMacro 的 350 槽展开与覆盖绑定顺序。" language: "zh-CN" primary_file: "core/classmacros.lua" related: @@ -100,13 +100,13 @@ macro.lua:CreateMacro(dynamic, static, special) `core/macro.lua` 用修饰键 × 基础键生成有序列表 `macroKind[1..N]`。覆盖绑定挂在独立 `bindingOwner` 上,便于 `ClearMacros` 只清本插件绑定。 -| 修饰键顺序 | 基础键(每组 39 个) | +| 修饰键顺序 | 基础键 | |---|---| -| `CTRL` → `ALT` → `SHIFT` → `ALT-CTRL` → `ALT-SHIFT` → `CTRL-SHIFT` → `ALT-CTRL-SHIFT` | 小键盘 0–9 / 小数点 / + / − / × / ÷;`F1–F3,F5–F12`(**无 F4**);`, . / ; ' [ ] \`;`7 8 9 0 =` | +| `CTRL` → `ALT` → `SHIFT` → `ALT-CTRL` → `ALT-SHIFT` → `CTRL-SHIFT` → `ALT-CTRL-SHIFT` | **第一段 39 键(槽 1–273)**:小键盘 0–9 / 小数点 / + / − / × / ÷;`F1–F3,F5–F12`(**无 F4**);`, . / ; ' [ ] \`;`7 8 9 0 =`。**第二段 11 键(槽 274–350)**:`-`、`INSERT` `DELETE` `HOME` `END` `PAGEUP` `PAGEDOWN`、`UP` `DOWN` `LEFT` `RIGHT`。不加反引号、`NUMPADENTER`。 | -- 总槽位数 = `7 × 39 = 273`。 +- 总槽位数 = `273 + 7 × 11 = 350`。 - 按钮名:`s1`、`s2`、…(与 `macroKind` 下标一致)。 -- 当前键池定义直接见 `Fuyutsui/core/keybinds.lua`(ID 1 = `CTRL-NUMPAD1`,以此类推)。 +- 当前键池定义直接见 `Fuyutsui/core/macro.lua`(ID 1 = `CTRL-NUMPAD1`,以此类推;新键从槽 274 的 `CTRL--` 起)。 **AI 改宏时**:关心的是解析后的 `dynamic` 占用多少槽、以及 static/special 数组下标对应的全局热键;不必手算,除非要核对外部程序按键映射。 @@ -431,10 +431,9 @@ staticSpells = { | 文件 | 职责 | 改宏时 | |---|---|---| -| `core/macro.lua` | 热键表、`ClearMacros`、顺序占键、`resolveMacroBody`、安全按钮 | 仅当要改分配规则/键池时才动 | +| `core/macro.lua` | 热键表、`ClearMacros`、顺序占键、`resolveMacroBody`、安全按钮 | 仅当要改分配规则/键池时才动;须与 C# `FuyutsuiKeymapConverter` 同序 | | `main.lua` | `ResolveDynamicSpells`、`LoadPlayerMacros` 选职业+专精 | 一般不动 | | `core/player.lua` | 加载/切换专精时调用 `LoadPlayerMacros` | 一般不动 | -| `Fuyutsui/core/keybinds.lua` | 当前热键 ID 与顺序权威源 | 键池变更时同步 | | `core/keybinds.lua` / `config.lua` keymap | 动作条扫描 → 像素协议 | **另一套**按键编码,与 ClassMacros 覆盖绑定无关 | | `class/*.lua` | ClassBlocks 色块 | 不放宏 | diff --git a/README.md b/README.md index efb920ee..42f78abf 100644 --- a/README.md +++ b/README.md @@ -174,7 +174,7 @@ Tools\ 辅助脚本 模块以 `模块名.json` 保存在我的文档目录 `{MyDocuments}/Shigure/module`。名称不能重复;加载时会递归扫描子目录,以兼容旧版布局。模块页保存时会写入当前 Shigure 版本。模块的 `Version` 必须与当前软件版本完整一致才会参与依赖导入、模块选择和运行;版本不一致或为空的模块仍在编辑器列表中显示为红色,可在检查并显式保存后升级到当前版本。职业和专精均已指定时,模块还会携带该专精的 `ClassBlocks`、职业 `spellsList`,以及该职业的通用/专精动态宏、静态宏和特殊宏。 -启动和“刷新模块”会把模块携带而本地缺少的配置与宏追加到项目 `Fuyutsui/`。光环、法术和技能列表都以 `spellId` 判断同一条目,不再按名称回退:光环任一 ID 重合即合并 ID 集合,`maxApps` 仅补本地空值;法术的布尔属性采用 true 补齐 false,最大充能和施法次数仅补本地空值;双方都有不同值时保留本地并提示冲突。技能列表同一 spellId 保留本地索引和名称,缺失 spellId 按模块快照追加,允许多个 spellId 共用同一索引。队伍依赖快照只为模块文件兼容保留,读取时不比较、不导入,也不修改本地队伍配置。发生新增后会自动重建 `config/keymap`、同步游戏插件并按需重启运行。导入前会按每项动态宏 30 个槽位、静态/特殊宏各 1 个槽位检查该职业所有受影响专精;合并结果超过 273 个槽位时,整个模块不会进入模块列表或运行时,本地 Lua 也不会被修改。模块文件仍保留在模块目录,清理宏后可刷新重试。 +启动和“刷新模块”会把模块携带而本地缺少的配置与宏追加到项目 `Fuyutsui/`。光环、法术和技能列表都以 `spellId` 判断同一条目,不再按名称回退:光环任一 ID 重合即合并 ID 集合,`maxApps` 仅补本地空值;法术的布尔属性采用 true 补齐 false,最大充能和施法次数仅补本地空值;双方都有不同值时保留本地并提示冲突。技能列表同一 spellId 保留本地索引和名称,缺失 spellId 按模块快照追加,允许多个 spellId 共用同一索引。队伍依赖快照只为模块文件兼容保留,读取时不比较、不导入,也不修改本地队伍配置。发生新增后会自动重建 `config/keymap`、同步游戏插件并按需重启运行。导入前会按每项动态宏 30 个槽位、静态/特殊宏各 1 个槽位检查该职业所有受影响专精;合并结果超过 350 个槽位时,整个模块不会进入模块列表或运行时,本地 Lua 也不会被修改。模块文件仍保留在模块目录,清理宏后可刷新重试。 模块匹配字段: