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 { /// /// 游戏配置编辑器 —— 可视化编辑5个配置文件 /// 1. effect_config.txt - 基础效果库(下拉框选项源) /// 2. macro_config.txt - 组合效果(宏) /// 3. gift_config.txt - 礼物/事件→效果映射 /// 4. command_config.txt - 弹幕指令触发词 /// 5. fkey_config.txt - F键→效果映射 /// 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 }; } // ═══════════════ 效果下拉刷新 ═══════════════ /// 刷新礼物/F键Tab的效果下拉框(从effect_config + macro_config读取) void RefreshEffectDropdowns() { var effectNames = new List(); // 从基础效果表读取 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(); 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(); 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 { 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); } } }