将宏键池从 273 扩到 350,并同步 Shigure 发键

在原 7×39 槽之后追加减号、导航六键和四个方向键(7×11),不插入 F4/反引号/NUMPADENTER,避免打乱已有热键。KeySender 改为从左解析修饰前缀以支持 CTRL--,导航和方向键按扩展键发送。

Co-authored-by: Wayne-Arasaka <waynebian01@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-09-02 03:48:05 +00:00
parent cf812626cb
commit ee8d3d854b
13 changed files with 149 additions and 56 deletions

View File

@@ -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 编辑器

View File

@@ -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 = {

View File

@@ -18,6 +18,13 @@ local keys = {
"7", "8", "9", "0", "="
}
-- 接在原 273 槽之后,避免把新主键插入 keys 导致后续修饰组错位。
-- 不含 F4ALT-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

View File

@@ -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<string, int> 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;
}

View File

@@ -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<string>(), null);
}
var rawParts = hotkey.Trim().Split('-', StringSplitOptions.RemoveEmptyEntries);
if (rawParts.Length == 0)
{
return (new List<string>(), null);
}
var mainKey = rawParts[^1];
// 从左消费修饰前缀剩余整段当主键。CTRL-- 不能按 '-' 切开,否则会丢掉减号。
var remaining = hotkey.Trim();
var mods = new List<string>();
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;
}

View File

@@ -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<string, int> CharacterKeys = new()

View File

@@ -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、标点和部分数字键,形成槽 1273
- 第二段在末尾追加 `-``INSERT``DELETE``HOME``END``PAGEUP``PAGEDOWN``UP``DOWN``LEFT``RIGHT`,再乘 7 种修饰符,形成槽 274350。不加反引号、`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 合并与宏创建入口。

View File

@@ -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 中只有 CTRLALTSHIFT 被保留为修饰键;未知的修饰 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` 仍取决于其消息循环和权限。

View File

@@ -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 保留单位 31raid1..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`:运行前等待队列并重启会话。

View File

@@ -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 键池和单位编号都需要生产者/消费者联合验证。
## 安全与信任边界

View File

@@ -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 键中间
## 范围

View File

@@ -1,6 +1,6 @@
---
title: "Fuyutsui core/classmacros.luaAI 宏规则参考"
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` | 小键盘 09 / 小数点 / + / / × / ÷;`F1F3,F5F12`**无 F4**`, . / ; ' [ ] \``7 8 9 0 =` |
| `CTRL``ALT``SHIFT``ALT-CTRL``ALT-SHIFT``CTRL-SHIFT``ALT-CTRL-SHIFT` | **第一段 39 键(槽 1273**小键盘 09 / 小数点 / + / / × / ÷;`F1F3,F5F12`**无 F4**`, . / ; ' [ ] \``7 8 9 0 =`。**第二段 11 键(槽 274350**`-``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 色块 | 不放宏 |

View File

@@ -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 也不会被修改。模块文件仍保留在模块目录,清理宏后可刷新重试。
模块匹配字段: