更新 ClassBlocksStore 和 ClassConfigEditorControl,支持编辑 spellsList;添加增压层数字段到状态和 UI 组件

This commit is contained in:
waynebian01
2026-08-14 15:34:28 +08:00
parent 24fe4f9729
commit 31365c23da
7 changed files with 396 additions and 20 deletions

View File

@@ -1,12 +1,13 @@
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using static Shigure.LuaLiteParser;
namespace Shigure;
/// <summary>
/// 读写 Fuyutsui class/*.lua 中的 ClassBlocksstates/auras/spells/group
/// 同时读 spellsList 供界面展示;保存时替换 ClassBlocks 表字面量,保留文件其余内容
/// 同时读 spellsList保存时替换 ClassBlocks 表字面量,并原位更新 spellsList 中已编辑的条目
/// </summary>
internal static class ClassBlocksStore
{
@@ -38,6 +39,9 @@ internal static class ClassBlocksStore
public long SpellId { get; set; }
public int Index { get; set; }
public string Name { get; set; } = string.Empty;
public long OriginalSpellId { get; set; }
public int OriginalIndex { get; set; }
public string OriginalName { get; set; } = string.Empty;
}
public sealed class SpecBlocks
@@ -164,7 +168,10 @@ internal static class ClassBlocksStore
{
SpellId = spellId,
Index = (int)indexValue.Value,
Name = name
Name = name,
OriginalSpellId = spellId,
OriginalIndex = (int)indexValue.Value,
OriginalName = name
});
}
@@ -178,10 +185,16 @@ internal static class ClassBlocksStore
throw new InvalidOperationException("当前文件仍是旧版稀疏索引 ClassBlocks无法用图形编辑器保存。");
}
var updated = UpdateSpellsListEntries(document.SourceText, document.SpellsList);
if (!TryExtractAssignedTable(updated, AssignmentName, out _, out var classBlocksStart, out var classBlocksEnd))
{
throw new InvalidOperationException("保存前无法重新定位 ClassBlocks 表。");
}
var serialized = SerializeClassBlocks(document.Specs);
var updated = document.SourceText[..document.TableStart]
updated = updated[..classBlocksStart]
+ serialized
+ document.SourceText[document.TableEndExclusive..];
+ updated[classBlocksEnd..];
File.WriteAllText(document.FilePath, updated, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
if (!TryExtractAssignedTable(updated, AssignmentName, out _, out var start, out var end))
@@ -192,6 +205,108 @@ internal static class ClassBlocksStore
document.SourceText = updated;
document.TableStart = start;
document.TableEndExclusive = end;
foreach (var spell in document.SpellsList)
{
spell.OriginalSpellId = spell.SpellId;
spell.OriginalIndex = spell.Index;
spell.OriginalName = spell.Name;
}
}
private static string UpdateSpellsListEntries(string source, IReadOnlyList<SpellsListEntry> entries)
{
var newEntries = entries.Where(entry => entry.OriginalSpellId == 0).ToArray();
var changedEntries = entries
.Where(entry => entry.OriginalSpellId != 0
&& (entry.SpellId != entry.OriginalSpellId
|| entry.Index != entry.OriginalIndex
|| !string.Equals(entry.Name, entry.OriginalName, StringComparison.Ordinal)))
.ToDictionary(entry => entry.OriginalSpellId);
if (changedEntries.Count == 0 && newEntries.Length == 0)
{
return source;
}
if (!TryExtractAssignedTable(source, SpellsListAssignmentName, out _, out var tableStart, out var tableEnd))
{
throw new InvalidOperationException($"当前文件中未找到 {SpellsListAssignmentName},无法保存技能列表。");
}
var tableText = source[tableStart..tableEnd];
var updatedOriginalIds = new HashSet<long>();
var pattern = new Regex(
"""^(?<prefix>[ \t]*\[[ \t]*)(?<spellId>\d+)(?<beforeIndex>[ \t]*\][ \t]*=[ \t]*\{[ \t]*index[ \t]*=[ \t]*)(?<index>\d+)(?<beforeName>[ \t]*,[ \t]*name[ \t]*=[ \t]*)(?<name>"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*')(?<suffix>[^\n]*)$""",
RegexOptions.Multiline | RegexOptions.CultureInvariant);
var updatedTable = pattern.Replace(tableText, match =>
{
if (!long.TryParse(match.Groups["spellId"].Value, NumberStyles.None, CultureInfo.InvariantCulture, out var originalSpellId)
|| !changedEntries.TryGetValue(originalSpellId, out var entry))
{
return match.Value;
}
updatedOriginalIds.Add(originalSpellId);
var quotedName = match.Groups["name"].Value;
var quote = quotedName[0];
return match.Groups["prefix"].Value
+ entry.SpellId.ToString(CultureInfo.InvariantCulture)
+ match.Groups["beforeIndex"].Value
+ entry.Index.ToString(CultureInfo.InvariantCulture)
+ match.Groups["beforeName"].Value
+ quote
+ EscapeLuaString(entry.Name, quote)
+ quote
+ match.Groups["suffix"].Value;
});
var missing = changedEntries.Keys.Where(id => !updatedOriginalIds.Contains(id)).ToArray();
if (missing.Length > 0)
{
throw new InvalidOperationException(
$"无法在 {SpellsListAssignmentName} 中定位法术 ID {string.Join(", ", missing)} 的原始条目。");
}
if (newEntries.Length > 0)
{
var newline = source.Contains("\r\n", StringComparison.Ordinal) ? "\r\n" : "\n";
var closingBraceIndex = updatedTable.Length - 1;
var closingLineBreak = updatedTable.LastIndexOf('\n', Math.Max(0, closingBraceIndex - 1));
var insertionIndex = closingLineBreak >= 0 ? closingLineBreak + 1 : closingBraceIndex;
var insertion = new StringBuilder();
if (closingLineBreak < 0)
{
insertion.Append(newline);
}
foreach (var entry in newEntries.OrderBy(entry => entry.Index))
{
insertion.Append(" [")
.Append(entry.SpellId.ToString(CultureInfo.InvariantCulture))
.Append("] = { index = ")
.Append(entry.Index.ToString(CultureInfo.InvariantCulture))
.Append(", name = \"")
.Append(EscapeLuaString(entry.Name, '"'))
.Append("\" },")
.Append(newline);
}
updatedTable = updatedTable.Insert(insertionIndex, insertion.ToString());
}
return source[..tableStart] + updatedTable + source[tableEnd..];
}
private static string EscapeLuaString(string value, char quote)
{
var escaped = value
.Replace("\\", "\\\\", StringComparison.Ordinal)
.Replace("\r", "\\r", StringComparison.Ordinal)
.Replace("\n", "\\n", StringComparison.Ordinal)
.Replace("\t", "\\t", StringComparison.Ordinal);
return quote == '\''
? escaped.Replace("'", "\\'", StringComparison.Ordinal)
: escaped.Replace("\"", "\\\"", StringComparison.Ordinal);
}
private static SpecBlocks ParseSpec(TableValue spec, out bool isModern)

View File

@@ -46,7 +46,7 @@ internal static class ClassStateCatalog
[
"法力值", "怒气值", "集中值", "能量值", "符文", "符文能量",
"星界能量", "漩涡值", "狂乱值", "奥术充能", "恶魔之怒", "痛苦值",
"连击点", "神圣能量", "精华能量", "灵魂碎片", "真气"
"连击点", "神圣能量", "精华能量", "灵魂碎片", "真气", "增压层数"
]),
(CategoryTarget,
[

View File

@@ -44,7 +44,7 @@ Shigure 是一个 Windows WinForms 桌面程序。它从目标窗口读取 Fuyut
- 置顶浮动条:显示程序名、当前职业图标颜色和逻辑状态,提供 `开启/关闭``设置``✕` 按钮。窗口可拖动和缩放,显示后自动启动运行循环。
- `通用`:设置触发键和发送模式;从项目 Fuyutsui 更新配置并同步游戏插件;按实时环境筛选、自动选择或手动指定模块。
- `配置`:直接编辑项目 `Fuyutsui/class/*.lua` 中的 `ClassBlocks`,包括状态、光环、法术和队伍字段。旧版稀疏索引格式只读,需先迁移到 `states/auras/spells/group` 格式。
- `配置`:直接编辑项目 `Fuyutsui/class/*.lua` 中的 `ClassBlocks`,包括状态、光环、法术和队伍字段;技能列表页可编辑 `Fuyutsui.spellsList` 中索引 1100 的法术 ID、索引和名称也可输入 spellId 与名称并自动分配空闲索引。旧版稀疏索引格式只读,需先迁移到 `states/auras/spells/group` 格式。
- `宏`:编辑项目 `Fuyutsui/core/classmacros.lua` 中各职业的动态宏、静态宏和特殊宏。动态宏每项占用 30 个团队点名槽位。
- `模块`:新建、编辑、删除本地模块,维护作者、推荐天赋、匹配条件、动态字段和有序规则。规则支持拖拽、上移、下移、复制与插入。
- `状态`:分栏显示基础状态、`auras``spells` 和模块计算出的动态单位/数值。

View File

@@ -88,7 +88,7 @@ verified_at: 2026-08-10
- 保存时只替换源文件中该 table literal表外文本保留表内部按 Store 支持的 schema 重新序列化,不承诺保留未知字段或原始格式。
- 保存是直接写回源 Lua不是临时文件原子替换也不自动备份。
- 旧稀疏专精会返回空编辑数据;若整个文档不是 modernStore 拒绝保存。混合 modern/legacy 文件尤其危险:全局可被判 modern但 legacy 专精仍为空,保存可能造成数据损失。
- `spellsList` 在 ClassBlocks UI 中只读
- `spellsList` 在 ClassBlocks UI 中可编辑索引 1100 的法术 ID、索引和名称保存时原位更新对应 Lua 条目,保留未显示的索引 101+ 条目与表内注释
## ClassBlocks → config

View File

@@ -95,7 +95,7 @@ verified_at: 2026-08-10
- 模块编辑器覆盖模块、Match、规则、动态单位、数量和数值调整并支持排序。
- 模块执行器支持规则 `Hotkey``Step`,但 `ModuleEditorControl.ReadRules` 保存时强制把二者写成空字符串。手工 JSON 中的非空值经 UI 打开再保存会丢失。
- 模块根级 `Enabled` 可被 UI/JSON保存但运行时忽略它见模块页。
- ClassBlocks 编辑器恢复前三个固定状态名为锚点/职业/专精;`spellsList` 只读
- ClassBlocks 编辑器恢复前三个固定状态名为锚点/职业/专精;`spellsList` 可编辑索引 1100 的条目并原位回写 Lua
- 旧稀疏 ClassBlocks 不是可靠的只读预览旧专精可显示为空Store 会拒绝非 modern 文档保存;混合格式需特别谨慎。
- ClassMacros 和 ClassBlocks 保存都会 canonical 重写目标表内部,未知字段/排版不保证保留。
- 条件编辑器只能表达当前简单 AND/OR 语言,不支持括号;动态名称禁止空、`.``$`、纯数字和冲突名称。

View File

@@ -5,7 +5,7 @@ namespace Shigure;
/// <summary>
/// 图形化编辑 Fuyutsui class/*.lua 的 ClassBlocksstates / auras / spells / group
/// 并展示同文件中的 spellsList。
/// 并编辑同文件中的 spellsList。
/// </summary>
public sealed class ClassConfigEditorControl : UserControl
{
@@ -25,6 +25,8 @@ public sealed class ClassConfigEditorControl : UserControl
private readonly DataGridView _aurasGrid = new();
private readonly DataGridView _spellsGrid = new();
private readonly DataGridView _spellsListGrid = new();
private readonly TextBox _newSpellIdBox = new();
private readonly TextBox _newSpellNameBox = new();
private readonly NumericUpDown _groupNumBox = new();
private readonly NumericUpDown _groupHealthBox = new();
private readonly NumericUpDown _groupRoleBox = new();
@@ -594,20 +596,92 @@ public sealed class ClassConfigEditorControl : UserControl
{
Dock = DockStyle.Fill,
ColumnCount = 1,
RowCount = 2,
BackColor = UiTheme.SurfaceRaised
RowCount = 3,
BackColor = UiTheme.SurfaceRaised,
Margin = new Padding(0),
Padding = new Padding(0)
};
panel.RowStyles.Add(new RowStyle(SizeType.Absolute, 34));
panel.RowStyles.Add(new RowStyle(SizeType.Absolute, 68));
panel.RowStyles.Add(new RowStyle(SizeType.Absolute, 30));
panel.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
var hint = CreateFieldCaption("来自当前职业 Lua 的 Fuyutsui.spellsList仅显示索引 1100只读。");
var addCard = new UiCardPanel
{
Dock = DockStyle.Fill,
ColumnCount = 4,
RowCount = 1,
Margin = new Padding(0, 0, 0, 10),
Padding = new Padding(12, 8, 12, 8)
};
addCard.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 92));
addCard.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 220));
addCard.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
addCard.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 96));
addCard.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
addCard.Controls.Add(new Label
{
Dock = DockStyle.Fill,
Text = "添加技能",
ForeColor = UiTheme.Text,
BackColor = Color.Transparent,
Font = new Font(Font.FontFamily, 9F, FontStyle.Bold, GraphicsUnit.Point),
TextAlign = ContentAlignment.MiddleLeft,
Margin = new Padding(0),
Padding = new Padding(8, 0, 0, 0)
}, 0, 0);
UiTheme.StyleTextBox(_newSpellIdBox);
_newSpellIdBox.Dock = DockStyle.None;
_newSpellIdBox.Anchor = AnchorStyles.Left | AnchorStyles.Right;
_newSpellIdBox.Margin = new Padding(0, 0, 8, 0);
_newSpellIdBox.Height = 30;
_newSpellIdBox.PlaceholderText = "spellId";
_newSpellIdBox.MaxLength = 20;
addCard.Controls.Add(_newSpellIdBox, 1, 0);
UiTheme.StyleTextBox(_newSpellNameBox);
_newSpellNameBox.Dock = DockStyle.None;
_newSpellNameBox.Anchor = AnchorStyles.Left | AnchorStyles.Right;
_newSpellNameBox.Margin = new Padding(0, 0, 8, 0);
_newSpellNameBox.Height = 30;
_newSpellNameBox.PlaceholderText = "名称";
addCard.Controls.Add(_newSpellNameBox, 2, 0);
var addControl = new UiPillTab("添加")
{
Selected = true,
Dock = DockStyle.None,
Anchor = AnchorStyles.Left | AnchorStyles.Right,
Margin = new Padding(0),
Height = 30,
MinimumSize = new Size(88, 30)
};
addControl.Click += (_, _) => AddSpellsListEntry();
addCard.Controls.Add(addControl, 3, 0);
void HandleAddOnEnter(object? _, KeyEventArgs e)
{
if (e.KeyCode != Keys.Enter)
{
return;
}
e.SuppressKeyPress = true;
AddSpellsListEntry();
}
_newSpellIdBox.KeyDown += HandleAddOnEnter;
_newSpellNameBox.KeyDown += HandleAddOnEnter;
panel.Controls.Add(addCard, 0, 0);
var hint = CreateFieldCaption("来自当前职业 Lua 的 Fuyutsui.spellsList仅编辑索引 1100保存后同步修改 Lua 对应条目。");
hint.Padding = new Padding(8, 0, 0, 0);
panel.Controls.Add(hint, 0, 0);
panel.Controls.Add(hint, 0, 1);
ConfigureGrid(_spellsListGrid);
_spellsListGrid.AllowUserToAddRows = false;
_spellsListGrid.ReadOnly = true;
_spellsListGrid.EditMode = DataGridViewEditMode.EditProgrammatically;
_spellsListGrid.CellValueChanged += (_, _) => MarkDirty();
_spellsListGrid.Columns.Add(new DataGridViewTextBoxColumn
{
Name = "SpellId",
@@ -629,7 +703,7 @@ public sealed class ClassConfigEditorControl : UserControl
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
SortMode = DataGridViewColumnSortMode.NotSortable
});
panel.Controls.Add(_spellsListGrid, 0, 1);
panel.Controls.Add(_spellsListGrid, 0, 2);
return panel;
}
@@ -1127,6 +1201,15 @@ public sealed class ClassConfigEditorControl : UserControl
}
SelectSpec(_specList.SelectedItem as SpecOption);
_suppressUi = true;
try
{
FillSpellsListGrid();
}
finally
{
_suppressUi = false;
}
}
private void RebuildSpecList(IReadOnlyList<SpecOption> options)
@@ -1186,7 +1269,6 @@ public sealed class ClassConfigEditorControl : UserControl
FillAurasGrid();
FillSpellsGrid();
FillGroupEditors();
FillSpellsListGrid();
}
finally
{
@@ -1390,6 +1472,8 @@ public sealed class ClassConfigEditorControl : UserControl
private void FillSpellsListGrid()
{
_spellsListGrid.Rows.Clear();
_newSpellIdBox.Clear();
_newSpellNameBox.Clear();
if (_currentDocument is null)
{
return;
@@ -1397,10 +1481,11 @@ public sealed class ClassConfigEditorControl : UserControl
foreach (var spell in _currentDocument.SpellsList.Where(spell => spell.Index is >= 1 and <= 100))
{
_spellsListGrid.Rows.Add(
var rowIndex = _spellsListGrid.Rows.Add(
spell.SpellId.ToString(CultureInfo.InvariantCulture),
spell.Index.ToString(CultureInfo.InvariantCulture),
spell.Name);
_spellsListGrid.Rows[rowIndex].Tag = spell;
}
}
@@ -1491,6 +1576,173 @@ public sealed class ClassConfigEditorControl : UserControl
WriteBackGroup();
}
private void WriteBackSpellsList()
{
if (_currentDocument is null)
{
return;
}
foreach (DataGridViewRow row in _spellsListGrid.Rows)
{
if (row.Tag is not ClassBlocksStore.SpellsListEntry entry)
{
continue;
}
entry.SpellId = long.Parse(
row.Cells["SpellId"].Value?.ToString()?.Trim() ?? "",
NumberStyles.None,
CultureInfo.InvariantCulture);
entry.Index = int.Parse(
row.Cells["Index"].Value?.ToString()?.Trim() ?? "",
NumberStyles.None,
CultureInfo.InvariantCulture);
entry.Name = row.Cells["Name"].Value?.ToString()?.Trim() ?? "";
}
}
private void AddSpellsListEntry()
{
if (_currentDocument is null)
{
MessageBox.Show("请先选择一个职业文件。", "技能列表", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
if (!_currentDocument.IsModernFormat)
{
MessageBox.Show("旧版稀疏索引格式暂不支持添加技能。", "技能列表", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
_spellsListGrid.EndEdit();
if (!TryValidateSpellsList(out var validationError))
{
MessageBox.Show(validationError, "技能列表", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
if (!long.TryParse(_newSpellIdBox.Text.Trim(), NumberStyles.None, CultureInfo.InvariantCulture, out var spellId)
|| spellId <= 0)
{
MessageBox.Show("spellId 必须是正整数。", "技能列表", MessageBoxButtons.OK, MessageBoxIcon.Warning);
_newSpellIdBox.Focus();
return;
}
var name = _newSpellNameBox.Text.Trim();
if (string.IsNullOrWhiteSpace(name))
{
MessageBox.Show("名称不能为空。", "技能列表", MessageBoxButtons.OK, MessageBoxIcon.Warning);
_newSpellNameBox.Focus();
return;
}
if (_currentDocument.SpellsList.Any(entry => entry.SpellId == spellId)
|| _spellsListGrid.Rows.Cast<DataGridViewRow>().Any(row =>
long.TryParse(row.Cells["SpellId"].Value?.ToString(), NumberStyles.None,
CultureInfo.InvariantCulture, out var existingSpellId)
&& existingSpellId == spellId))
{
MessageBox.Show($"法术 ID {spellId} 已存在。", "技能列表", MessageBoxButtons.OK, MessageBoxIcon.Warning);
_newSpellIdBox.Focus();
return;
}
var usedIndices = _spellsListGrid.Rows
.Cast<DataGridViewRow>()
.Select(row => int.TryParse(row.Cells["Index"].Value?.ToString(), NumberStyles.None,
CultureInfo.InvariantCulture, out var value) ? value : 0)
.Where(value => value is >= 1 and <= 100)
.ToHashSet();
var nextIndex = Enumerable.Range(1, 100).FirstOrDefault(index => !usedIndices.Contains(index));
if (nextIndex == 0)
{
MessageBox.Show("索引 1100 已全部使用,无法继续添加技能。", "技能列表", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
var entry = new ClassBlocksStore.SpellsListEntry
{
SpellId = spellId,
Index = nextIndex,
Name = name
};
_currentDocument.SpellsList.Add(entry);
var rowIndex = _spellsListGrid.Rows.Add(
spellId.ToString(CultureInfo.InvariantCulture),
nextIndex.ToString(CultureInfo.InvariantCulture),
name);
var row = _spellsListGrid.Rows[rowIndex];
row.Tag = entry;
_spellsListGrid.ClearSelection();
row.Selected = true;
_spellsListGrid.CurrentCell = row.Cells["SpellId"];
_spellsListGrid.FirstDisplayedScrollingRowIndex = rowIndex;
_newSpellIdBox.Clear();
_newSpellNameBox.Clear();
_newSpellIdBox.Focus();
MarkDirty();
}
private bool TryValidateSpellsList(out string error)
{
error = string.Empty;
if (_currentDocument is null)
{
return true;
}
var editedIds = new HashSet<long>();
foreach (DataGridViewRow row in _spellsListGrid.Rows)
{
var rowNumber = row.Index + 1;
if (!long.TryParse(row.Cells["SpellId"].Value?.ToString()?.Trim(), NumberStyles.None,
CultureInfo.InvariantCulture, out var spellId)
|| spellId <= 0)
{
error = $"技能列表第 {rowNumber} 行的法术 ID 必须是正整数。";
return false;
}
if (!int.TryParse(row.Cells["Index"].Value?.ToString()?.Trim(), NumberStyles.None,
CultureInfo.InvariantCulture, out var index)
|| index is < 1 or > 100)
{
error = $"技能列表第 {rowNumber} 行的索引必须是 1100 的整数。";
return false;
}
if (string.IsNullOrWhiteSpace(row.Cells["Name"].Value?.ToString()))
{
error = $"技能列表第 {rowNumber} 行的名称不能为空。";
return false;
}
if (!editedIds.Add(spellId))
{
error = $"技能列表中的法术 ID {spellId} 重复。";
return false;
}
}
var hiddenIds = _currentDocument.SpellsList
.Where(entry => entry.Index is < 1 or > 100)
.Select(entry => entry.SpellId)
.ToHashSet();
var conflictId = editedIds.FirstOrDefault(hiddenIds.Contains);
if (conflictId != 0)
{
error = $"法术 ID {conflictId} 已被技能列表中索引 101+ 的条目使用。";
return false;
}
return true;
}
private void WriteBackStatesCategory(string category)
{
if (_currentSpec is null)
@@ -1705,8 +1957,17 @@ public sealed class ClassConfigEditorControl : UserControl
var localSaved = false;
try
{
_spellsListGrid.EndEdit();
if (!TryValidateSpellsList(out var validationError))
{
MessageBox.Show(validationError, "技能列表", MessageBoxButtons.OK, MessageBoxIcon.Warning);
_statusLabel.Text = validationError;
return;
}
// 切换分类前把当前状态表写回。
CommitCurrentSpecFromUi();
WriteBackSpellsList();
ClassBlocksStore.Save(_currentDocument);
localSaved = true;
SetDirty(false);

View File

@@ -784,7 +784,7 @@ public sealed class StatusForm : Form
[
"法力值", "怒气值", "集中值", "能量值", "符文", "符文能量",
"星界能量", "漩涡值", "狂乱值", "恶魔之怒", "痛苦值",
"连击点", "神圣能量", "精华能量", "灵魂碎片", "真气"
"连击点", "神圣能量", "精华能量", "灵魂碎片", "真气", "增压层数"
],
150), 1, 0);
fields.Controls.Add(CreateAboutFieldCard(