mirror of
https://github.com/waynebian01/Shigure.git
synced 2026-09-03 07:15:24 +08:00
优化特殊宏的注释管理,确保技能名由用户手动填写并固定为无目标、无宏条件;更新相关文档和界面显示。
This commit is contained in:
@@ -126,7 +126,7 @@ Fuyutsui.ClassMacros = {
|
||||
|
||||
},
|
||||
specialSpells = {
|
||||
"/stopcasting",
|
||||
"/stopcasting", -- 停止施法
|
||||
},
|
||||
},
|
||||
|
||||
@@ -235,8 +235,8 @@ Fuyutsui.ClassMacros = {
|
||||
"[@target]驱散魔法",
|
||||
},
|
||||
specialSpells = {
|
||||
"/castsequence reset=0.4 真言术:耀,x",
|
||||
"/stopcasting",
|
||||
"/castsequence reset=0.4 真言术:耀,x", -- 真言术:耀
|
||||
"/stopcasting", -- 停止施法
|
||||
},
|
||||
},
|
||||
|
||||
@@ -292,7 +292,7 @@ Fuyutsui.ClassMacros = {
|
||||
"[@cursor]枯萎凋零",
|
||||
},
|
||||
specialSpells = {
|
||||
"/castsequence reset=1 死亡之握,x",
|
||||
"/castsequence reset=1 死亡之握,x", -- 死亡之握
|
||||
},
|
||||
},
|
||||
|
||||
@@ -346,7 +346,7 @@ Fuyutsui.ClassMacros = {
|
||||
"[@player]治疗之雨",
|
||||
},
|
||||
specialSpells = {
|
||||
"/stopcasting",
|
||||
"/stopcasting", -- 停止施法
|
||||
},
|
||||
},
|
||||
|
||||
@@ -579,8 +579,8 @@ Fuyutsui.ClassMacros = {
|
||||
"[nostance:1]熊形态",
|
||||
},
|
||||
specialSpells = {
|
||||
"/cancelaura [spec:4]猎豹形态\n/cast 万灵之召",
|
||||
"/castsequence reset=0.5 铁鬃,x",
|
||||
"/cancelaura [spec:4]猎豹形态\n/cast 万灵之召", -- 万灵之召
|
||||
"/castsequence reset=0.5 铁鬃,x", -- 铁鬃
|
||||
},
|
||||
},
|
||||
|
||||
@@ -634,8 +634,8 @@ Fuyutsui.ClassMacros = {
|
||||
"破裂",
|
||||
},
|
||||
specialSpells = {
|
||||
"/castsequence reset=0.5 恶魔变形,x",
|
||||
"/castsequence reset=0.5 烈火烙印,x",
|
||||
"/castsequence reset=0.5 恶魔变形,x", -- 恶魔变形
|
||||
"/castsequence reset=0.5 烈火烙印,x", -- 烈火烙印
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -54,6 +54,10 @@ internal static class ClassMacrosStore
|
||||
public sealed class ArrayEntry
|
||||
{
|
||||
public string Text { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Lua 同行注释。静态宏沿用注释/技能名覆盖语义;特殊宏专门用它保存手工技能名。
|
||||
/// </summary>
|
||||
public string? Comment { get; set; }
|
||||
}
|
||||
|
||||
|
||||
@@ -238,7 +238,8 @@ internal static partial class FuyutsuiKeymapConverter
|
||||
}
|
||||
}
|
||||
|
||||
if (entry is { Body.Length: > 0 }
|
||||
if (isStaticEntry
|
||||
&& entry is { Body.Length: > 0 }
|
||||
&& IsWeakSpellName(spell)
|
||||
&& TryGetExistingSpellName(existingSpellNames, specId, i, out var preserved)
|
||||
&& !string.IsNullOrWhiteSpace(preserved)
|
||||
@@ -328,20 +329,11 @@ internal static partial class FuyutsuiKeymapConverter
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析特殊宏:方括号中以 @ 开头的首个单位作为 unit,技能沿用宏技能推导(castsequence 只取逗号前首项)。
|
||||
/// 特殊宏不解析宏正文。技能名必须由编辑器手工填写并保存在同行注释中,
|
||||
/// keymap 固定使用无目标、无宏条件。
|
||||
/// </summary>
|
||||
internal static ParsedMacro ParseSpecialMacro(string raw, string? comment = null)
|
||||
{
|
||||
// 标准 WoW 条件允许单位出现在其它条件之后,例如 [known:123,@cursor]。
|
||||
var target = StaticTargetRegex().Match(raw);
|
||||
var unit = target.Success
|
||||
? ResolveUnitName(target.Groups["unit"].Value)
|
||||
: ReservedUnit.None;
|
||||
|
||||
var spell = ResolveSpellName(new MacroEntry(raw, comment));
|
||||
spell = SplitTopLevel(spell, ',').FirstOrDefault()?.Trim() ?? string.Empty;
|
||||
return new ParsedMacro(unit, spell, ResolveConditions(raw));
|
||||
}
|
||||
internal static ParsedMacro ParseSpecialMacro(string _, string? comment = null)
|
||||
=> new(ReservedUnit.None, comment?.Trim() ?? string.Empty, string.Empty);
|
||||
|
||||
/// <summary>方括号中以 @ 开头的是目标,其余逗号分隔项作为只读条件摘要。</summary>
|
||||
private static string ResolveConditions(string raw)
|
||||
|
||||
@@ -1094,8 +1094,8 @@ internal sealed class ModuleDependencyService
|
||||
public bool HasConfigChanges => ConfigAdded > 0 || ConfigUpdated > 0;
|
||||
}
|
||||
|
||||
// 静态宏和特殊宏可以解析成相同的目标/技能/条件,但仍是两个独立槽位。
|
||||
// 例如“恶魔变形”和“/castsequence reset=0.5 恶魔变形,x”必须分别去重。
|
||||
// 静态宏按解析出的目标/技能/条件去重;特殊宏只按手工技能名去重,
|
||||
// 但两类宏仍是两个独立槽位。
|
||||
private readonly record struct MacroIdentity(bool IsSpecial, int Unit, string Spell, string Condition);
|
||||
}
|
||||
|
||||
|
||||
@@ -160,6 +160,8 @@ public sealed class ModuleMacrosSnapshot
|
||||
public sealed class ModuleMacroEntrySnapshot
|
||||
{
|
||||
public string Text { get; set; } = string.Empty;
|
||||
|
||||
// 对特殊宏表示必须手工维护的技能名,并以 Lua 行尾注释持久化。
|
||||
public string? Comment { get; set; }
|
||||
|
||||
public ModuleMacroEntrySnapshot Clone() => (ModuleMacroEntrySnapshot)MemberwiseClone();
|
||||
|
||||
@@ -121,7 +121,8 @@ verified_at: 2026-08-10
|
||||
- 动态宏项每项预留/消耗 30 个单位位置,然后是静态宏和特殊宏;超过容量会警告并截断。
|
||||
- 旧 flat 格式或 common+spec 格式都可转换;专精输出是“common + 当前 spec + static + special”的完整映射,以适配运行时不合并顶层的语义。
|
||||
- party1..4 映射组员槽 2..5;原始 `@player` 动态语义映射组员槽 1;显式中文/player 保留单位 31;raid1..30 映射 1..30。
|
||||
- 方括号中非 `@` 条件汇总为 macroCondition;尾注释可以覆盖从宏文本推导的 spell 名。
|
||||
- dynamic/static 仍按宏文本生成映射,方括号中非 `@` 条件汇总为 macroCondition,尾注释可以覆盖推导出的 spell 名。
|
||||
- special 不再分析宏正文:尾注释必须作为手工技能名,映射固定为 `unit=0`、空 macroCondition;宏编辑器因此只显示可编辑技能名和完整宏,不显示目标与条件。
|
||||
- 能识别 stopcasting、castsequence、最后一个 cast 和 item 等受支持形式;不等于完整 WoW 宏解释器。
|
||||
- 输出包含全部 273 项,包括空项,以维持索引位置。
|
||||
|
||||
|
||||
@@ -98,6 +98,7 @@ verified_at: 2026-08-10
|
||||
- ClassBlocks 编辑器恢复前三个固定状态名为锚点/职业/专精;`spellsList` 可编辑索引 1–100 的条目并原位回写 Lua。
|
||||
- 旧稀疏 ClassBlocks 不是可靠的只读预览:旧专精可显示为空,Store 会拒绝非 modern 文档保存;混合格式需特别谨慎。
|
||||
- ClassMacros 和 ClassBlocks 保存都会 canonical 重写目标表内部,未知字段/排版不保证保留。
|
||||
- ClassMacros 特殊宏的技能名由用户手工填写并保存为 Lua 行尾注释;界面不展示目标和条件,转换器也不从特殊宏正文推导这些字段。
|
||||
- 条件编辑器只能表达当前简单 AND/OR 语言,不支持括号;动态名称禁止空、`.`、`$`、纯数字和冲突名称。
|
||||
|
||||
## 模块选择的 UI 语义
|
||||
|
||||
@@ -80,7 +80,7 @@ dynamicSpells(每项展开 30 个团队槽)
|
||||
| `Fuyutsui.MacroBodies` | 可复用的命名宏体 | 被条目按名称解析,不单独占槽 |
|
||||
| `dynamicSpells` | 对队伍/团队单位展开的技能 | 每个条目连续占 30 个单位槽 |
|
||||
| `staticSpells` | 普通技能或可解析宏条目 | 每项占一个槽,空字符串保留位置 |
|
||||
| `specialSpells` | 完整特殊宏文本 | 在 static 之后逐项占槽,保留顺序和注释语义 |
|
||||
| `specialSpells` | 完整特殊宏文本;行尾注释是手工技能名 | 在 static 之后逐项占槽,保留顺序;Shigure 固定映射为无目标、无宏条件 |
|
||||
|
||||
`LoadPlayerMacros` 先取得当前职业和专精,把 `dynamicSpells.common` 与当前 `[specIndex]` 连接,再交给 `CreateMacro`。
|
||||
|
||||
@@ -131,7 +131,7 @@ keymap 是生成物;宏 Lua 与两端一致的展开算法才是来源。
|
||||
|
||||
1. `ClassMacrosStore` 与 `LuaLiteParser` 读取同一 `ClassMacros` 表,并保留数组空槽和行尾注释。
|
||||
2. `FuyutsuiKeymapConverter` 按 dynamic 每项 30 槽,再 static、special 的顺序遍历同一 key pool。
|
||||
3. 转换器从直接技能或宏文本中提取技能名、单位与可选宏条件,写入职业 keymap。
|
||||
3. 转换器从 dynamic/static 条目提取技能名、单位与可选宏条件;special 不解析正文,只读取行尾注释中的手工技能名,并固定写成无目标、无宏条件。
|
||||
4. `KeymapService` 根据当前职业/专精选择数据,并以 unit、spell、condition 查找 hotkey。
|
||||
5. module 可直接提供 hotkey,或提供技能/动态单位后通过 keymap 解析;特殊动作可能先从 `GameState` 解析实际技能。
|
||||
6. `ShigureRuntime` 应用规则延迟和逻辑延迟后调用 `KeySender`;目标窗口收到的键应命中 Fuyutsui 覆盖绑定或游戏动作条。
|
||||
|
||||
@@ -305,13 +305,14 @@ Fuyutsui.MacroBodies = {
|
||||
|
||||
```lua
|
||||
specialSpells = {
|
||||
"/castsequence reset=0.5 死亡之握,x",
|
||||
"/stopcasting",
|
||||
"/castsequence reset=0.5 死亡之握,x", -- 死亡之握
|
||||
"/stopcasting", -- 停止施法
|
||||
}
|
||||
```
|
||||
|
||||
- 同样是**数组**,接在该职业**全部** `staticSpells` 之后依次占键。
|
||||
- 也走 `resolveMacroBody`:可用完整 `/...` 文本,或 MacroBodies 名称,或普通法术名(会加 `/cast`)。
|
||||
- Fuyutsui 只使用字符串宏体;Shigure 把同行 `--` 注释作为必须手工维护的技能名,不分析特殊宏正文,并固定生成无目标、无宏条件的 keymap 条目。
|
||||
- 当前仓库多数职业 `specialSpells = {}`;历史上与 static 同槽的特殊宏(sequence / stopcasting 等)已直接写在 `staticSpells` 对应位置(以 `/` 开头),以保持键位。
|
||||
|
||||
### 8.2 何时用 special 而不是写进 static
|
||||
|
||||
@@ -47,7 +47,7 @@ Shigure 是一个 Windows WinForms 桌面程序。它从目标窗口读取 Fuyut
|
||||
- 置顶浮动条:显示程序名、当前职业图标颜色和逻辑状态,提供 `开启/关闭`、`设置`、`✕` 按钮。窗口可拖动和缩放,显示后自动启动运行循环。
|
||||
- `通用`:设置触发键和发送模式;从项目 Fuyutsui 更新配置并同步游戏插件;按实时环境选择模块,或按职业、专精、英雄天赋和队伍类型指定默认模块;从 GitHub 按需下载或更新技能图标数据包。
|
||||
- `配置`:直接编辑项目 `Fuyutsui/class/*.lua` 中的 `ClassBlocks`,包括状态、光环、法术和队伍字段;技能列表页可编辑 `Fuyutsui.spellsList` 中索引 1–100 的法术 ID、索引和名称,也可输入 spellId 与名称并自动分配空闲索引。旧版稀疏索引格式只读,需先迁移到 `states/auras/spells/group` 格式。
|
||||
- `宏`:编辑项目 `Fuyutsui/core/classmacros.lua` 中各职业的动态宏、静态宏和特殊宏。动态宏每项占用 30 个团队点名槽位。
|
||||
- `宏`:编辑项目 `Fuyutsui/core/classmacros.lua` 中各职业的动态宏、静态宏和特殊宏。动态宏每项占用 30 个团队点名槽位;特殊宏的技能名必须手工填写,不从宏正文解析,生成映射时固定为无目标、无宏条件。
|
||||
- `模块`:新建、编辑、删除本地模块,维护作者、推荐天赋、匹配条件、动态字段和有序规则。规则支持拖拽、上移、下移、复制与插入。
|
||||
- `状态`:分栏显示基础状态、`auras`、`spells` 和模块计算出的动态单位/数值。
|
||||
- `队伍`:显示 `group` 中当前队伍成员及扫描字段摘要。
|
||||
|
||||
@@ -261,7 +261,13 @@ public sealed class ClassMacrosEditorControl : UserControl
|
||||
{
|
||||
BuildDynamicPage(),
|
||||
BuildArrayPage(_staticGrid, "class-macros-static", "完整宏", "注释", showParsedMacro: true),
|
||||
BuildArrayPage(_specialGrid, "class-macros-special", "完整宏", "注释", showParsedMacro: true)
|
||||
BuildArrayPage(
|
||||
_specialGrid,
|
||||
"class-macros-special",
|
||||
"完整宏",
|
||||
"注释",
|
||||
showParsedMacro: false,
|
||||
editSpecialSpell: true)
|
||||
};
|
||||
foreach (var page in pages)
|
||||
{
|
||||
@@ -401,7 +407,8 @@ public sealed class ClassMacrosEditorControl : UserControl
|
||||
string cacheKey,
|
||||
string textHeader,
|
||||
string commentHeader,
|
||||
bool showParsedMacro)
|
||||
bool showParsedMacro,
|
||||
bool editSpecialSpell = false)
|
||||
{
|
||||
var panel = new TableLayoutPanel
|
||||
{
|
||||
@@ -445,6 +452,15 @@ public sealed class ClassMacrosEditorControl : UserControl
|
||||
ReadOnly = true
|
||||
});
|
||||
}
|
||||
else if (editSpecialSpell)
|
||||
{
|
||||
grid.Columns.Add(new DataGridViewTextBoxColumn
|
||||
{
|
||||
Name = "Spell",
|
||||
HeaderText = "技能",
|
||||
Width = 180
|
||||
});
|
||||
}
|
||||
|
||||
grid.Columns.Add(new DataGridViewTextBoxColumn
|
||||
{
|
||||
@@ -452,7 +468,10 @@ public sealed class ClassMacrosEditorControl : UserControl
|
||||
HeaderText = textHeader,
|
||||
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill
|
||||
});
|
||||
grid.Columns.Add(new DataGridViewTextBoxColumn { Name = "Comment", HeaderText = commentHeader, Width = 180 });
|
||||
if (!editSpecialSpell)
|
||||
{
|
||||
grid.Columns.Add(new DataGridViewTextBoxColumn { Name = "Comment", HeaderText = commentHeader, Width = 180 });
|
||||
}
|
||||
grid.Columns.Add(CreateDeleteColumn());
|
||||
WireGrid(grid);
|
||||
panel.Controls.Add(grid, 0, 0);
|
||||
@@ -472,7 +491,7 @@ public sealed class ClassMacrosEditorControl : UserControl
|
||||
return;
|
||||
}
|
||||
|
||||
if ((grid == _staticGrid || grid == _specialGrid)
|
||||
if (grid == _staticGrid
|
||||
&& e.RowIndex >= 0
|
||||
&& e.ColumnIndex >= 0
|
||||
&& grid.Columns[e.ColumnIndex].Name is "Text" or "Comment")
|
||||
@@ -955,7 +974,8 @@ public sealed class ClassMacrosEditorControl : UserControl
|
||||
}
|
||||
|
||||
var text = row.Cells["Text"].Value?.ToString() ?? "";
|
||||
var comment = row.Cells["Comment"].Value?.ToString()?.Trim();
|
||||
var metadataColumn = grid.Columns.Contains("Comment") ? "Comment" : "Spell";
|
||||
var comment = row.Cells[metadataColumn].Value?.ToString()?.Trim();
|
||||
target.Add(new ClassMacrosStore.ArrayEntry
|
||||
{
|
||||
Text = text.Replace("\r\n", "\n", StringComparison.Ordinal),
|
||||
@@ -1137,9 +1157,16 @@ public sealed class ClassMacrosEditorControl : UserControl
|
||||
var row = grid.Rows[rowIndex];
|
||||
row.Cells["Index"].Value = (i + 1).ToString(CultureInfo.InvariantCulture);
|
||||
row.Cells["Text"].Value = entry.Text;
|
||||
row.Cells["Comment"].Value = entry.Comment ?? "";
|
||||
if (grid == _specialGrid)
|
||||
{
|
||||
row.Cells["Spell"].Value = entry.Comment ?? "";
|
||||
}
|
||||
else
|
||||
{
|
||||
row.Cells["Comment"].Value = entry.Comment ?? "";
|
||||
}
|
||||
row.Cells["Delete"].Value = "×";
|
||||
if (grid == _staticGrid || grid == _specialGrid)
|
||||
if (grid == _staticGrid)
|
||||
{
|
||||
UpdateMacroDisplay(grid, row);
|
||||
}
|
||||
@@ -1158,16 +1185,12 @@ public sealed class ClassMacrosEditorControl : UserControl
|
||||
|
||||
var text = row.Cells["Text"].Value?.ToString() ?? "";
|
||||
var comment = row.Cells["Comment"].Value?.ToString();
|
||||
var parsed = grid == _specialGrid
|
||||
? FuyutsuiKeymapConverter.ParseSpecialMacro(text, comment)
|
||||
: FuyutsuiKeymapConverter.ParseStaticMacro(text, comment);
|
||||
var parsed = FuyutsuiKeymapConverter.ParseStaticMacro(text, comment);
|
||||
|
||||
_updatingDerivedColumns = true;
|
||||
try
|
||||
{
|
||||
row.Cells["Unit"].Value = grid == _specialGrid
|
||||
? ReservedUnit.ToDisplayText(parsed.Unit)
|
||||
: parsed.Unit.ToString(CultureInfo.InvariantCulture);
|
||||
row.Cells["Unit"].Value = parsed.Unit.ToString(CultureInfo.InvariantCulture);
|
||||
row.Cells["Spell"].Value = parsed.Spell;
|
||||
row.Cells["Condition"].Value = MacroConditionText.ToDisplayText(parsed.Condition);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user