Files
DanMuRead/BarrageGrab/Forms/ConfigEditorForm.cs
T

524 lines
22 KiB
C#
Raw Normal View History

2026-08-04 00:10:33 +08:00
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace BarrageGrab.Forms
{
/// <summary>
/// 游戏配置编辑器 —— 可视化编辑5个配置文件
/// 1. effect_config.txt - 基础效果库(下拉框选项源)
/// 2. macro_config.txt - 组合效果(宏)
/// 3. gift_config.txt - 礼物/事件→效果映射
/// 4. command_config.txt - 弹幕指令触发词
/// 5. fkey_config.txt - F键→效果映射
/// </summary>
public partial class ConfigEditorForm : Form
{
private DataGridView dgvEffects, dgvMacros, dgvGifts, dgvCommands, dgvFkeys;
private TabControl tabMain;
private string ConfigDir { get { return Program.ConfigDir; } }
private Label lblStatus;
// 文件路径
private string EffectPath => Program.EffectPath;
private string MacroPath => Program.MacroPath;
private string GiftPath => Program.GiftPath;
private string CmdPath => Program.CommandPath;
private string FkeyPath => Program.FkeyPath;
private readonly string[] FKeyOptions = { "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12" };
// CommandType 列表(用于指令Tab)
private readonly string[] CommandTypes = {
"SelectState", "SpawnTroop", "Gold", "Population", "Destiny",
"Mobilize", "Attack", "Alliance", "ResearchTech", "SetIdeology",
"ApplyBuff", "Info"
};
public ConfigEditorForm()
{
InitializeComponent();
this.Text = "游戏配置编辑器 (5文件)";
this.ClientSize = new Size(950, 650);
this.StartPosition = FormStartPosition.CenterParent;
BuildUI();
LoadAll();
}
private void InitializeComponent()
{
this.SuspendLayout();
this.AutoScaleMode = AutoScaleMode.Font;
this.ResumeLayout(false);
}
// ═══════════════ UI 构建 ═══════════════
void BuildUI()
{
tabMain = new TabControl { Dock = DockStyle.Fill, Name = "tabMain" };
this.Controls.Add(tabMain);
// Tab1: 基础效果
var tabEffect = new TabPage { Text = "基础效果", Name = "tabEffect" };
dgvEffects = CreateEffectGrid();
tabEffect.Controls.Add(dgvEffects);
tabEffect.Controls.Add(CreateButtonPanel(() => AddRow(dgvEffects, "新效果", "出兵 3"),
"格式: 效果名称|游戏指令 | 原子效果,下拉框选项源"));
tabEffect.Controls[1].Dock = DockStyle.Bottom;
dgvEffects.Dock = DockStyle.Fill;
tabMain.TabPages.Add(tabEffect);
// Tab2: 组合宏
var tabMacro = new TabPage { Text = "组合宏", Name = "tabMacro" };
dgvMacros = CreateMacroGrid();
tabMacro.Controls.Add(dgvMacros);
tabMacro.Controls.Add(CreateButtonPanel(() => AddRow(dgvMacros, "新宏", "", "出兵 5|金币 100"),
"格式: 宏名称|触发词(可空)|指令1|指令2 | 指令支持$1 $2 $*"));
tabMacro.Controls[1].Dock = DockStyle.Bottom;
dgvMacros.Dock = DockStyle.Fill;
tabMain.TabPages.Add(tabMacro);
// Tab3: 礼物事件
var tabGift = new TabPage { Text = "礼物事件", Name = "tabGift" };
dgvGifts = CreateGiftGrid();
tabGift.Controls.Add(dgvGifts);
tabGift.Controls.Add(CreateButtonPanel(() => AddRow(dgvGifts, "新礼物", "", "{0} 送了礼物", ""),
"格式: 关键词|效果名称|公告|间隔 | 效果名称从下拉框选"));
tabGift.Controls[1].Dock = DockStyle.Bottom;
dgvGifts.Dock = DockStyle.Fill;
tabMain.TabPages.Add(tabGift);
// Tab4: 弹幕指令
var tabCmd = new TabPage { Text = "弹幕指令", Name = "tabCmd" };
dgvCommands = CreateCommandGrid();
tabCmd.Controls.Add(dgvCommands);
tabCmd.Controls.Add(CreateButtonPanel(() => AddRow(dgvCommands, "SelectState", "选择,选,select"),
"格式: [CommandType]|触发词1,触发词2 | 宏在'组合宏'Tab配置"));
tabCmd.Controls[1].Dock = DockStyle.Bottom;
dgvCommands.Dock = DockStyle.Fill;
tabMain.TabPages.Add(tabCmd);
// Tab5: F键
var tabFkey = new TabPage { Text = "F键", Name = "tabFkey" };
dgvFkeys = CreateFkeyGrid();
tabFkey.Controls.Add(dgvFkeys);
tabFkey.Controls.Add(CreateButtonPanel(() => AddRow(dgvFkeys, "F2", ""),
"格式: F键|效果名称 | 效果名称从下拉框选 | F1固定初始化"));
tabFkey.Controls[1].Dock = DockStyle.Bottom;
dgvFkeys.Dock = DockStyle.Fill;
tabMain.TabPages.Add(tabFkey);
}
Panel CreateButtonPanel(Action addAction, string hint)
{
var p = new Panel { Height = 40 };
var btnAdd = new Button { Text = "+ 新增", Left = 5, Top = 8, Width = 70 };
btnAdd.Click += (s, e) => addAction();
p.Controls.Add(btnAdd);
var btnDel = new Button { Text = "删除选中", Left = 80, Top = 8, Width = 70 };
btnDel.Click += (s, e) =>
{
var dgv = GetCurrentGrid();
if (dgv?.SelectedRows.Count > 0)
foreach (DataGridViewRow row in dgv.SelectedRows)
if (!row.IsNewRow) dgv.Rows.Remove(row);
};
p.Controls.Add(btnDel);
var btnReload = new Button { Text = "重新加载", Left = 155, Top = 8, Width = 80 };
btnReload.Click += (s, e) => { LoadAll(); };
p.Controls.Add(btnReload);
var btnSave = new Button { Text = "全部保存", Left = 240, Top = 8, Width = 90, BackColor = Color.LightGreen };
btnSave.Click += (s, e) => SaveAll();
p.Controls.Add(btnSave);
lblStatus = new Label { AutoSize = true, Left = 340, Top = 12, Text = "" };
p.Controls.Add(lblStatus);
var lblHint = new Label { AutoSize = true, Left = 500, Top = 12, ForeColor = Color.Gray, Text = hint };
p.Controls.Add(lblHint);
return p;
}
DataGridView GetCurrentGrid()
{
switch (tabMain.SelectedIndex)
{
case 0: return dgvEffects;
case 1: return dgvMacros;
case 2: return dgvGifts;
case 3: return dgvCommands;
case 4: return dgvFkeys;
default: return null;
}
}
void AddRow(DataGridView dgv, params object[] values) => dgv.Rows.Add(values);
// ═══════════════ 表格创建 ═══════════════
DataGridView CreateEffectGrid()
{
var dgv = CreateBaseGrid("dgvEffects");
dgv.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "效果名称", Name = "Name", Width = 150 });
dgv.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "游戏指令", Name = "Command", Width = 300 });
return dgv;
}
DataGridView CreateMacroGrid()
{
var dgv = CreateBaseGrid("dgvMacros");
dgv.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "宏名称", Name = "Name", Width = 120 });
dgv.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "触发词(逗号分隔,可空)", Name = "Triggers", Width = 150 });
dgv.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "指令(多条用|分隔,支持$1 $2 $*)", Name = "Commands", Width = 350 });
return dgv;
}
DataGridView CreateGiftGrid()
{
var dgv = CreateBaseGrid("dgvGifts");
dgv.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "关键词(礼物名/事件)", Name = "Keyword", Width = 120 });
var colEffect = new DataGridViewComboBoxColumn { HeaderText = "效果名称", Name = "Effect", Width = 180 };
colEffect.AutoComplete = true;
dgv.Columns.Add(colEffect);
dgv.DataError += (s, e) => { e.ThrowException = false; };
dgv.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "公告文本", Name = "Announce", Width = 200 });
dgv.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "间隔", Name = "Interval", Width = 50 });
return dgv;
}
DataGridView CreateCommandGrid()
{
var dgv = CreateBaseGrid("dgvCommands");
var colType = new DataGridViewComboBoxColumn { HeaderText = "CommandType", Name = "CmdType", Width = 120 };
colType.Items.AddRange(CommandTypes);
dgv.Columns.Add(colType);
dgv.DataError += (s, e) => { e.ThrowException = false; };
dgv.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "触发词(逗号分隔)", Name = "Triggers", Width = 400 });
return dgv;
}
DataGridView CreateFkeyGrid()
{
var dgv = CreateBaseGrid("dgvFkeys");
var colKey = new DataGridViewComboBoxColumn { HeaderText = "F键", Name = "FKey", Width = 80 };
colKey.Items.AddRange(FKeyOptions);
dgv.Columns.Add(colKey);
var colEffect = new DataGridViewComboBoxColumn { HeaderText = "效果名称", Name = "Effect", Width = 250 };
colEffect.AutoComplete = true;
dgv.Columns.Add(colEffect);
dgv.DataError += (s, e) => { e.ThrowException = false; };
return dgv;
}
DataGridView CreateBaseGrid(string name)
{
return new DataGridView
{
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill,
AllowUserToAddRows = false,
AllowUserToDeleteRows = false,
RowHeadersVisible = false,
SelectionMode = DataGridViewSelectionMode.FullRowSelect,
Name = name
};
}
// ═══════════════ 效果下拉刷新 ═══════════════
/// <summary>刷新礼物/F键Tab的效果下拉框(从effect_config + macro_config读取)</summary>
void RefreshEffectDropdowns()
{
var effectNames = new List<string>();
// 从基础效果表读取
foreach (DataGridViewRow row in dgvEffects.Rows)
{
if (row.IsNewRow) continue;
var name = row.Cells["Name"].Value?.ToString();
if (!string.IsNullOrEmpty(name)) effectNames.Add(name);
}
// 从宏表读取
foreach (DataGridViewRow row in dgvMacros.Rows)
{
if (row.IsNewRow) continue;
var name = row.Cells["Name"].Value?.ToString();
if (!string.IsNullOrEmpty(name)) effectNames.Add(name);
}
// 刷新礼物Tab
var giftCol = dgvGifts.Columns["Effect"] as DataGridViewComboBoxColumn;
if (giftCol != null)
{
var oldValues = new List<string>();
foreach (DataGridViewRow row in dgvGifts.Rows)
{
if (row.IsNewRow) continue;
var v = row.Cells["Effect"].Value?.ToString();
if (!string.IsNullOrEmpty(v)) oldValues.Add(v);
}
giftCol.Items.Clear();
foreach (var n in effectNames) giftCol.Items.Add(n);
foreach (var v in oldValues)
if (!giftCol.Items.Contains(v)) giftCol.Items.Add(v);
}
// 刷新F键Tab
var fkeyCol = dgvFkeys.Columns["Effect"] as DataGridViewComboBoxColumn;
if (fkeyCol != null)
{
var oldValues = new List<string>();
foreach (DataGridViewRow row in dgvFkeys.Rows)
{
if (row.IsNewRow) continue;
var v = row.Cells["Effect"].Value?.ToString();
if (!string.IsNullOrEmpty(v)) oldValues.Add(v);
}
fkeyCol.Items.Clear();
foreach (var n in effectNames) fkeyCol.Items.Add(n);
foreach (var v in oldValues)
if (!fkeyCol.Items.Contains(v)) fkeyCol.Items.Add(v);
}
}
// ═══════════════ 加载 ═══════════════
void LoadAll()
{
if (!Directory.Exists(ConfigDir)) Directory.CreateDirectory(ConfigDir);
LoadEffects();
LoadMacros();
LoadGifts();
LoadCommands();
LoadFkeys();
RefreshEffectDropdowns();
lblStatus.Text = "配置已加载";
lblStatus.ForeColor = Color.Black;
}
void LoadEffects()
{
dgvEffects.Rows.Clear();
if (!File.Exists(EffectPath)) return;
foreach (var line in File.ReadAllLines(EffectPath, Encoding.UTF8))
{
var t = line.Trim();
if (string.IsNullOrEmpty(t) || t.StartsWith("#")) continue;
int pipe = t.IndexOf('|');
if (pipe <= 0) continue;
dgvEffects.Rows.Add(t.Substring(0, pipe).Trim(), t.Substring(pipe + 1).Trim());
}
}
void LoadMacros()
{
dgvMacros.Rows.Clear();
if (!File.Exists(MacroPath)) return;
foreach (var line in File.ReadAllLines(MacroPath, Encoding.UTF8))
{
var t = line.Trim();
if (string.IsNullOrEmpty(t) || t.StartsWith("#")) continue;
var parts = t.Split('|');
if (parts.Length < 3) continue;
var name = parts[0].Trim();
var triggers = parts[1].Trim();
var cmds = string.Join("|", parts.Skip(2).Select(p => p.Trim()));
dgvMacros.Rows.Add(name, triggers, cmds);
}
}
void LoadGifts()
{
dgvGifts.Rows.Clear();
if (!File.Exists(GiftPath)) return;
foreach (var line in File.ReadAllLines(GiftPath, Encoding.UTF8))
{
var t = line.Trim();
if (string.IsNullOrEmpty(t) || t.StartsWith("#")) continue;
var parts = t.Split('|');
if (parts.Length < 2) continue;
var keyword = parts[0].Trim();
var effect = parts[1].Trim();
var announce = parts.Length > 2 ? parts[2].Trim() : "";
var interval = parts.Length > 3 ? parts[3].Trim() : "";
dgvGifts.Rows.Add(keyword, effect, announce, interval);
}
}
void LoadCommands()
{
dgvCommands.Rows.Clear();
if (!File.Exists(CmdPath)) return;
foreach (var line in File.ReadAllLines(CmdPath, Encoding.UTF8))
{
var t = line.Trim();
if (string.IsNullOrEmpty(t) || t.StartsWith("#")) continue;
if (!t.StartsWith("[")) continue;
int end = t.IndexOf(']');
if (end <= 1) continue;
var typeName = t.Substring(1, end - 1).Trim();
var triggers = t.Substring(end + 1).TrimStart('|').Trim();
dgvCommands.Rows.Add(typeName, triggers);
}
}
void LoadFkeys()
{
dgvFkeys.Rows.Clear();
if (!File.Exists(FkeyPath))
{
// 默认值
var defaults = new[] {
("F2","出兵5"), ("F3","金币300"), ("F4","出兵3"), ("F5","金币500"),
("F6","攻击力x2_30s"), ("F7","人口1000"), ("F8","天命200"),
("F9","科技全开"), ("F10","特征全开"), ("F11","动员5"), ("F12","大礼包")
};
foreach (var d in defaults) dgvFkeys.Rows.Add(d.Item1, d.Item2);
return;
}
foreach (var line in File.ReadAllLines(FkeyPath, Encoding.UTF8))
{
var t = line.Trim();
if (string.IsNullOrEmpty(t) || t.StartsWith("#")) continue;
int pipe = t.IndexOf('|');
if (pipe <= 0) continue;
dgvFkeys.Rows.Add(t.Substring(0, pipe).Trim(), t.Substring(pipe + 1).Trim());
}
}
// ═══════════════ 保存 ═══════════════
void SaveAll()
{
try
{
if (!Directory.Exists(ConfigDir)) Directory.CreateDirectory(ConfigDir);
SaveEffects();
SaveMacros();
SaveGifts();
SaveCommands();
SaveFkeys();
RefreshEffectDropdowns();
lblStatus.Text = "全部保存成功";
lblStatus.ForeColor = Color.Green;
}
catch (Exception ex)
{
lblStatus.Text = $"保存失败: {ex.Message}";
lblStatus.ForeColor = Color.Red;
}
}
void SaveEffects()
{
var sb = new StringBuilder();
sb.AppendLine("# 游戏基础效果配置 v1");
sb.AppendLine("# 格式: 效果名称|游戏指令");
sb.AppendLine("# 一个效果 = 一条原子指令(多指令组合用 macro_config)");
sb.AppendLine();
foreach (DataGridViewRow row in dgvEffects.Rows)
{
if (row.IsNewRow) continue;
var name = row.Cells["Name"].Value?.ToString() ?? "";
var cmd = row.Cells["Command"].Value?.ToString() ?? "";
if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(cmd))
sb.AppendLine($"{name}|{cmd}");
}
File.WriteAllText(EffectPath, sb.ToString(), Encoding.UTF8);
}
void SaveMacros()
{
var sb = new StringBuilder();
sb.AppendLine("# 游戏组合效果(宏)配置 v1");
sb.AppendLine("# 格式: 宏名称|触发词1,触发词2,...|指令1|指令2|...");
sb.AppendLine("# 触发词可选(留空则只能被礼物/F键引用)");
sb.AppendLine("# 指令支持 $1 $2 $* 参数替换(仅弹幕触发时)");
sb.AppendLine();
foreach (DataGridViewRow row in dgvMacros.Rows)
{
if (row.IsNewRow) continue;
var name = row.Cells["Name"].Value?.ToString() ?? "";
var triggers = row.Cells["Triggers"].Value?.ToString() ?? "";
var cmds = row.Cells["Commands"].Value?.ToString() ?? "";
if (string.IsNullOrEmpty(name)) continue;
// cmds 中的 | 是指令分隔符,保持原样
sb.AppendLine($"{name}|{triggers}|{cmds}");
}
File.WriteAllText(MacroPath, sb.ToString(), Encoding.UTF8);
}
void SaveGifts()
{
var sb = new StringBuilder();
sb.AppendLine("# 游戏礼物/事件配置 v1");
sb.AppendLine("# 格式: 关键词|效果名称|公告文本|触发间隔(可选)");
sb.AppendLine("# 效果名称引用 effect_config 或 macro_config(下拉框选择)");
sb.AppendLine("# {0}=昵称 {1}=数量");
sb.AppendLine("# 特殊关键词: 点赞/关注/进房/分享/粉丝团");
sb.AppendLine();
foreach (DataGridViewRow row in dgvGifts.Rows)
{
if (row.IsNewRow) continue;
var keyword = row.Cells["Keyword"].Value?.ToString() ?? "";
var effect = row.Cells["Effect"].Value?.ToString() ?? "";
var msg = row.Cells["Announce"].Value?.ToString() ?? "";
var interval = row.Cells["Interval"].Value?.ToString() ?? "";
if (string.IsNullOrEmpty(keyword) || string.IsNullOrEmpty(effect)) continue;
var parts = new List<string> { keyword, effect };
if (!string.IsNullOrEmpty(msg)) parts.Add(msg);
if (!string.IsNullOrEmpty(interval)) parts.Add(interval);
sb.AppendLine(string.Join("|", parts));
}
File.WriteAllText(GiftPath, sb.ToString(), Encoding.UTF8);
}
void SaveCommands()
{
var sb = new StringBuilder();
sb.AppendLine("# 游戏弹幕指令触发词配置 v1");
sb.AppendLine("# 格式: [CommandType]|触发词1,触发词2,...");
sb.AppendLine("# 注: 宏指令在 macro_config.txt 配置");
sb.AppendLine();
foreach (DataGridViewRow row in dgvCommands.Rows)
{
if (row.IsNewRow) continue;
var type = row.Cells["CmdType"].Value?.ToString() ?? "";
var triggers = row.Cells["Triggers"].Value?.ToString() ?? "";
if (string.IsNullOrEmpty(type)) continue;
sb.AppendLine($"[{type}]|{triggers}");
}
File.WriteAllText(CmdPath, sb.ToString(), Encoding.UTF8);
}
void SaveFkeys()
{
var sb = new StringBuilder();
sb.AppendLine("# 游戏 F键配置 v1");
sb.AppendLine("# 格式: F键|效果名称");
sb.AppendLine("# 效果名称引用 effect_config 或 macro_config");
sb.AppendLine("# F1 固定为初始化,不可修改");
sb.AppendLine();
foreach (DataGridViewRow row in dgvFkeys.Rows)
{
if (row.IsNewRow) continue;
var key = row.Cells["FKey"].Value?.ToString() ?? "";
var effect = row.Cells["Effect"].Value?.ToString() ?? "";
if (string.IsNullOrEmpty(key)) continue;
sb.AppendLine($"{key}|{effect}");
}
File.WriteAllText(FkeyPath, sb.ToString(), Encoding.UTF8);
}
}
}