diff --git a/Infrastructure/ClassStateCatalog.cs b/Infrastructure/ClassStateCatalog.cs index 3cd4070d..29918cd2 100644 --- a/Infrastructure/ClassStateCatalog.cs +++ b/Infrastructure/ClassStateCatalog.cs @@ -32,7 +32,7 @@ internal static class ClassStateCatalog "首领战", "难度", "英雄天赋", "施法目标", "施法技能", "敌人数量", "敌人数-无仇恨", "敌人数-有仇恨","施法", "引导", "蓄力", "蓄力层数", "酒池", "符文", "姿态", - "救赎之魂1", "救赎之魂2", + "救赎之魂1", "救赎之魂2", "神圣军备" ]), (CategoryConfig, [ diff --git a/Infrastructure/FuyutsuiKeymapConverter.cs b/Infrastructure/FuyutsuiKeymapConverter.cs index 8da0e50f..5dbceb3b 100644 --- a/Infrastructure/FuyutsuiKeymapConverter.cs +++ b/Infrastructure/FuyutsuiKeymapConverter.cs @@ -357,9 +357,26 @@ internal static partial class FuyutsuiKeymapConverter private static int ResolveUnitName(string raw) { - return raw.Trim().TrimStart('@').ToLowerInvariant() switch + var normalized = raw.Trim().TrimStart('@').ToLowerInvariant(); + if (normalized.StartsWith("party", StringComparison.Ordinal) + && int.TryParse(normalized[5..], out var partyIndex) + && partyIndex is >= 1 and <= 4) { - "player" or "玩家" or "31" => ReservedUnit.Player, + // Fuyutsui 队伍槽位:player=1,party1..4=2..5。 + return partyIndex + 1; + } + + if (normalized.StartsWith("raid", StringComparison.Ordinal) + && int.TryParse(normalized[4..], out var raidIndex) + && raidIndex is >= 1 and <= 30) + { + return raidIndex; + } + + return normalized switch + { + "player" => 1, + "玩家" or "31" => ReservedUnit.Player, "target" or "目标" or "32" => ReservedUnit.Target, "focus" or "焦点" or "33" => ReservedUnit.Focus, "cursor" or "地面" or "34" => ReservedUnit.Cursor, @@ -652,7 +669,7 @@ internal static partial class FuyutsuiKeymapConverter [GeneratedRegex(@"\[[^\]]*\]", RegexOptions.CultureInvariant)] private static partial Regex ConditionRegex(); - [GeneratedRegex(@"\[[^\]]*@(?cursor|target|focus|player|mouseover)\b[^\]]*\]", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)] + [GeneratedRegex(@"\[[^\]]*@(?cursor|target|focus|player|mouseover|party[1-4]|raid(?:[1-9]|[12][0-9]|30))\b[^\]]*\]", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)] private static partial Regex StaticTargetRegex(); } diff --git a/UI/ClassMacrosEditorControl.cs b/UI/ClassMacrosEditorControl.cs index e75a9cce..bfbb6270 100644 --- a/UI/ClassMacrosEditorControl.cs +++ b/UI/ClassMacrosEditorControl.cs @@ -471,6 +471,8 @@ public sealed class ClassMacrosEditorControl : UserControl private void WireGrid(DataGridView grid) { grid.CellContentClick += HandleDeleteClick; + grid.CellFormatting += (_, e) => FormatMacroLineBreaks(grid, e); + grid.CellParsing += (_, e) => ParseMacroLineBreaks(grid, e); grid.CellValueChanged += (_, e) => { if (_updatingDerivedColumns) @@ -540,6 +542,40 @@ public sealed class ClassMacrosEditorControl : UserControl grid.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None; } + // 宏文本底层保留真实换行(供解析及保存),表格中以字面量 \n 单行显示。 + private void FormatMacroLineBreaks(DataGridView grid, DataGridViewCellFormattingEventArgs e) + { + if ((grid != _staticGrid && grid != _specialGrid) + || e.RowIndex < 0 + || e.ColumnIndex < 0 + || grid.Columns[e.ColumnIndex].Name != "Text" + || e.Value is not string text) + { + return; + } + + e.Value = text + .Replace("\r\n", "\n", StringComparison.Ordinal) + .Replace('\r', '\n') + .Replace("\n", "\\n", StringComparison.Ordinal); + e.FormattingApplied = true; + } + + private void ParseMacroLineBreaks(DataGridView grid, DataGridViewCellParsingEventArgs e) + { + if ((grid != _staticGrid && grid != _specialGrid) + || e.RowIndex < 0 + || e.ColumnIndex < 0 + || grid.Columns[e.ColumnIndex].Name != "Text" + || e.Value is not string text) + { + return; + } + + e.Value = text.Replace("\\n", "\n", StringComparison.Ordinal); + e.ParsingApplied = true; + } + private static DataGridViewButtonColumn CreateDeleteColumn() => new() { diff --git a/UI/StatusForm.cs b/UI/StatusForm.cs index 021c48c0..2f1a4a91 100644 --- a/UI/StatusForm.cs +++ b/UI/StatusForm.cs @@ -434,7 +434,7 @@ public sealed class StatusForm : Form "队伍类型", "队伍人数", "首领战", "难度", "英雄天赋", "施法目标", "施法技能", "敌人数量", "敌人数-无仇恨", "敌人数-有仇恨", "施法", "引导", "蓄力", "蓄力层数", "酒池", "符文", "姿态", - "救赎之魂1", "救赎之魂2", + "救赎之魂1", "救赎之魂2", "神圣军备" ], 150), 0, 0); fields.Controls.Add(CreateAboutFieldCard(