using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; using JXHelper.Common; using JXHelper.Config; using JXHelper.Core; using JXHelper.Platform; namespace JXHelper.UI; public record KeyOption(string DisplayName, int VirtualKey) { public override string ToString() => DisplayName; } public partial class MainForm : Form { private readonly BotCoordinator _coordinator = new(); private readonly BotConfig _baseConfig = BotConfig.Load(); private readonly List _keyOptions = new(); private readonly List _buffKeyOptions = new(); private readonly List _potionKeyOptions = new(); private bool _isBindingUi = false; public MainForm() { try { System.IO.File.AppendAllText(@"c:\Projects\JX\logs\startup.log", $"[{DateTime.Now}] In MainForm ctor, InitializeComponent...\n"); InitializeComponent(); bool isAdmin = Program.IsAdministrator(); Text = $"JXHelper v2.6 {(isAdmin ? "[Admin]" : "[Chưa Admin]")}"; System.IO.File.AppendAllText(@"c:\Projects\JX\logs\startup.log", $"[{DateTime.Now}] In MainForm ctor, SetupKeyOptions...\n"); SetupKeyOptions(); System.IO.File.AppendAllText(@"c:\Projects\JX\logs\startup.log", $"[{DateTime.Now}] In MainForm ctor, RegisterEventHandlers...\n"); RegisterEventHandlers(); System.IO.File.AppendAllText(@"c:\Projects\JX\logs\startup.log", $"[{DateTime.Now}] In MainForm ctor, RefreshAccountsList...\n"); RefreshAccountsList(); tabControl.SelectedTab = tabCombat; System.IO.File.AppendAllText(@"c:\Projects\JX\logs\startup.log", $"[{DateTime.Now}] In MainForm ctor, DONE.\n"); } catch (Exception ex) { System.IO.File.AppendAllText(@"c:\Projects\JX\logs\startup.log", $"[{DateTime.Now}] CRASH IN MAINFORM CTOR: {ex}\n"); throw; } } public void SelectLogisticsTab() { tabControl.SelectedTab = tabLogistics; tabLogistics.AutoScrollPosition = new Point(0, 200); } private void SetupKeyOptions() { // 1. Phím Buff chiêu thức (JX1 mechanics: Ưu tiên Chuột phải [VK_RBUTTON], phím số 1..9, Q, W, E, A, S, D - CẤM F1..F12) _buffKeyOptions.Add(new KeyOption("C.Phải", 0x02)); for (int i = 1; i <= 9; i++) { _buffKeyOptions.Add(new KeyOption($"Số {i}", 0x30 + i)); } _buffKeyOptions.Add(new KeyOption("Phím Q", 0x51)); _buffKeyOptions.Add(new KeyOption("Phím W", 0x57)); _buffKeyOptions.Add(new KeyOption("Phím E", 0x45)); _buffKeyOptions.Add(new KeyOption("Phím A", 0x41)); _buffKeyOptions.Add(new KeyOption("Phím S", 0x53)); _buffKeyOptions.Add(new KeyOption("Phím D", 0x44)); // 2. Phím Bơm Máu / Mana (Quick Slots 1..9, Q, W, E) for (int i = 1; i <= 9; i++) { _potionKeyOptions.Add(new KeyOption($"Số {i}", 0x30 + i)); } _potionKeyOptions.Add(new KeyOption("Phím Q", 0x51)); _potionKeyOptions.Add(new KeyOption("Phím W", 0x57)); _potionKeyOptions.Add(new KeyOption("Phím E", 0x45)); // 3. Phím Chung / Hồi Thành Phù (Hỗ trợ cả phím số và F1..F12) for (int i = 1; i <= 9; i++) { _keyOptions.Add(new KeyOption($"Số {i}", 0x30 + i)); } for (int i = 1; i <= 12; i++) { _keyOptions.Add(new KeyOption($"F{i}", 0x70 + (i - 1))); } cboKeyPhu.Items.AddRange(_keyOptions.ToArray()); cboHpKey1.Items.AddRange(_potionKeyOptions.ToArray()); cboHpKey2.Items.AddRange(_potionKeyOptions.ToArray()); cboMpKey1.Items.AddRange(_potionKeyOptions.ToArray()); cboMpKey2.Items.AddRange(_potionKeyOptions.ToArray()); cboBuff1Key.Items.AddRange(_buffKeyOptions.ToArray()); cboBuff2Key.Items.AddRange(_buffKeyOptions.ToArray()); cboBuff3Key.Items.AddRange(_buffKeyOptions.ToArray()); btnScanSkills.Click += (s, e) => ScanSkillsForCurrentSession(); cboSect.SelectedIndexChanged += (s, e) => OnSectSelectionChanged(); cboMainAttackSkill.SelectedIndexChanged += (s, e) => OnMainAttackSkillChanged(); cboBuff1Skill.SelectedIndexChanged += (s, e) => OnBuffSkillChanged(cboBuff1Skill, numBuff1Sec); cboBuff2Skill.SelectedIndexChanged += (s, e) => OnBuffSkillChanged(cboBuff2Skill, numBuff2Sec); cboBuff3Skill.SelectedIndexChanged += (s, e) => OnBuffSkillChanged(cboBuff3Skill, numBuff3Sec); EnableCustomDrawingForCombo(cboMainAttackSkill); EnableCustomDrawingForCombo(cboSect); EnableCustomDrawingForCombo(cboBuff1Skill); EnableCustomDrawingForCombo(cboBuff2Skill); EnableCustomDrawingForCombo(cboBuff3Skill); EnableCustomDrawingForCombo(cboBuff1Key); EnableCustomDrawingForCombo(cboBuff2Key); EnableCustomDrawingForCombo(cboBuff3Key); EnableCustomDrawingForCombo(cboAttackRadius); } private void EnableCustomDrawingForCombo(ComboBox cbo) { cbo.DrawMode = DrawMode.OwnerDrawFixed; cbo.DrawItem += (s, e) => { if (e.Index < 0 || e.Index >= cbo.Items.Count) return; e.DrawBackground(); string text = cbo.Items[e.Index]?.ToString() ?? ""; using var brush = new SolidBrush(e.ForeColor); e.Graphics.DrawString(text, e.Font ?? cbo.Font, brush, e.Bounds.X + 2, e.Bounds.Y + 1); e.DrawFocusRectangle(); }; } private void BindKeyCombo(ComboBox cbo, int vk, List options, int defaultIndex = 0) { var found = options.Find(k => k.VirtualKey == vk); cbo.SelectedItem = found ?? (options.Count > defaultIndex ? options[defaultIndex] : options.FirstOrDefault()); } private void BindKeyCombo(ComboBox cbo, int vk, int defaultIndex = 0) { BindKeyCombo(cbo, vk, _keyOptions, defaultIndex); } public void BindSessionConfigToUi(BotConfig config) { _isBindingUi = true; try { // 1. Tab Luyện Công (L.công) chkAutoAttack.Checked = config.AutoAttackEnabled; if (config.AttackMode == 1) radAttackLeft.Checked = true; else radAttackRight.Checked = true; numAttackInterval.Value = Math.Clamp(config.AttackIntervalMs, numAttackInterval.Minimum, numAttackInterval.Maximum); cboAttackRadius.SelectedIndex = config.AttackRadius switch { 200 => 1, 400 => 2, 800 => 3, _ => 0 }; chkFollowLeader.Checked = config.FollowLeaderEnabled; chkAutoHorse.Checked = config.AutoHorseEnabled; chkAutoLoot.Checked = config.AutoLootEnabled; string targetChar = !string.IsNullOrEmpty(config.CharacterName) ? config.CharacterName : (!string.IsNullOrEmpty(config.LastSelectedTitle) ? config.LastSelectedTitle : "BoCuaCacCon"); string? savedSect = !string.IsNullOrEmpty(config.DetectedSect) ? config.DetectedSect : null; var profile = SkillScanner.ScanCharacterSkills(targetChar, savedSect); string activeSect = !string.IsNullOrEmpty(profile.Sect) ? profile.Sect : (savedSect ?? "Cái Bang"); if (cboSect.Items.Contains(activeSect)) { cboSect.SelectedItem = activeSect; } else { cboSect.SelectedIndex = 0; } config.DetectedSect = activeSect; // Bảng Kỹ Năng: Chiêu thức công kích chính PopulateAttackSkillCombo(cboMainAttackSkill, profile, config.MainAttackSkillName); if (cboMainAttackSkill.SelectedItem != null && !cboMainAttackSkill.SelectedItem.ToString()!.StartsWith("(")) { config.MainAttackSkillName = cboMainAttackSkill.SelectedItem.ToString()!; } // Hiển thị danh sách kỹ năng bị động (Passive) if (profile.PassiveSkills.Count > 0) { string pNames = string.Join(", ", profile.PassiveSkills.Select(p => $"{p.Name} ({p.Level})")); lblPassiveSkills.Text = $"Bị động: {pNames} (Tự kích hoạt)"; } else { lblPassiveSkills.Text = "Bị động: (Chưa học / Không có)"; } PopulateBuffSkillCombo(cboBuff1Skill, profile, config.Buff1SkillName, 0); PopulateBuffSkillCombo(cboBuff2Skill, profile, config.Buff2SkillName, 1); PopulateBuffSkillCombo(cboBuff3Skill, profile, config.Buff3SkillName, 2); chkBuff1.Checked = config.Buff1Enabled || (string.IsNullOrEmpty(config.Buff1SkillName) && profile.ActiveBuffSkills.Count > 0); BindKeyCombo(cboBuff1Key, config.Buff1Key > 0 ? config.Buff1Key : 0x02, _buffKeyOptions, 0); // Chuột phải mặc định int defSec1 = profile.ActiveBuffSkills.Count > 0 ? profile.ActiveBuffSkills[0].CooldownSec : 60; numBuff1Sec.Value = Math.Clamp(config.Buff1IntervalSeconds > 5 ? config.Buff1IntervalSeconds : defSec1, numBuff1Sec.Minimum, numBuff1Sec.Maximum); chkBuff2.Checked = config.Buff2Enabled || (string.IsNullOrEmpty(config.Buff2SkillName) && profile.ActiveBuffSkills.Count > 1); BindKeyCombo(cboBuff2Key, config.Buff2Key > 0 ? config.Buff2Key : 0x02, _buffKeyOptions, 0); // Chuột phải mặc định int defSec2 = profile.ActiveBuffSkills.Count > 1 ? profile.ActiveBuffSkills[1].CooldownSec : 80; numBuff2Sec.Value = Math.Clamp(config.Buff2IntervalSeconds > 5 ? config.Buff2IntervalSeconds : defSec2, numBuff2Sec.Minimum, numBuff2Sec.Maximum); chkBuff3.Checked = config.Buff3Enabled || (string.IsNullOrEmpty(config.Buff3SkillName) && profile.ActiveBuffSkills.Count > 2); BindKeyCombo(cboBuff3Key, config.Buff3Key > 0 ? config.Buff3Key : 0x02, _buffKeyOptions, 0); // Chuột phải mặc định int defSec3 = profile.ActiveBuffSkills.Count > 2 ? profile.ActiveBuffSkills[2].CooldownSec : 90; numBuff3Sec.Value = Math.Clamp(config.Buff3IntervalSeconds > 5 ? config.Buff3IntervalSeconds : defSec3, numBuff3Sec.Minimum, numBuff3Sec.Maximum); // 2. Tab Phục Hồi (P.hồi) chkHp1.Checked = config.HpPump1Enabled; BindKeyCombo(cboHpKey1, config.HpPump1Key, _potionKeyOptions, 0); // Phím 1 numHpDelay1.Value = Math.Clamp(config.HpPump1DelayMs, numHpDelay1.Minimum, numHpDelay1.Maximum); chkHp2.Checked = config.HpPump2Enabled; BindKeyCombo(cboHpKey2, config.HpPump2Key, _potionKeyOptions, 1); // Phím 2 numHpDelay2.Value = Math.Clamp(config.HpPump2DelayMs, numHpDelay2.Minimum, numHpDelay2.Maximum); chkMp1.Checked = config.MpPump1Enabled; BindKeyCombo(cboMpKey1, config.MpPump1Key, _potionKeyOptions, 2); // Phím 3 numMpDelay1.Value = Math.Clamp(config.MpPump1DelayMs, numMpDelay1.Minimum, numMpDelay1.Maximum); chkMp2.Checked = config.MpPump2Enabled; BindKeyCombo(cboMpKey2, config.MpPump2Key, _potionKeyOptions, 3); // Phím 4 numMpDelay2.Value = Math.Clamp(config.MpPump2DelayMs, numMpDelay2.Minimum, numMpDelay2.Maximum); chkAlarmTdp.Checked = config.AlarmOnTdp; chkNewbiePouch.Checked = config.AutoNewbiePouchEnabled; numNewbiePouchSec.Value = Math.Clamp(config.NewbiePouchIntervalSeconds, numNewbiePouchSec.Minimum, numNewbiePouchSec.Maximum); // 3. Tab Điều Khiển (Đ.khiển) chkAutoParty.Checked = config.AutoPartyEnabled; chkFollowLeaderTab.Checked = config.FollowLeaderEnabled; txtLeaderName.Text = !string.IsNullOrEmpty(config.LeaderName) ? config.LeaderName : "ConCuaBoMe"; chkFollowCrossMap.Checked = config.AutoFollowCrossMap; chkAutoTtl.Checked = config.AutoTtlEnabled; numTtlInterval.Value = Math.Clamp(config.TtlIntervalMinutes, numTtlInterval.Minimum, numTtlInterval.Maximum); chkScheduledExit.Checked = config.ScheduledExitGameEnabled; numExitMin.Value = Math.Clamp(config.ExitGameMinutes, numExitMin.Minimum, numExitMin.Maximum); chkScheduledShutdown.Checked = config.ScheduledShutdownEnabled; numShutdownMin.Value = Math.Clamp(config.ShutdownMinutes, numShutdownMin.Minimum, numShutdownMin.Maximum); // 4. Tab Tọa Độ & Tuần Tra (T.độ) chkPatrol.Checked = config.PatrolEnabled; numPatrolSec.Value = Math.Clamp(config.PatrolIntervalSeconds, numPatrolSec.Minimum, numPatrolSec.Maximum); PopulateWaypointsList(config.WaypointsData); // 5. Tab Hậu Cần (Thành thị & Cấp 60+) chkPeriodic.Checked = config.PeriodicReturnEnabled; numInterval.Value = Math.Clamp(config.ReturnIntervalMinutes, numInterval.Minimum, numInterval.Maximum); BindKeyCombo(cboKeyPhu, config.KeyPhuVirtualCode, 4); // Phím 5 if (config.RepairMode == 1) radBlacksmith.Checked = true; else radToolBag.Checked = true; numBsX.Value = Math.Clamp(config.BlacksmithNpcX, numBsX.Minimum, numBsX.Maximum); numBsY.Value = Math.Clamp(config.BlacksmithNpcY, numBsY.Minimum, numBsY.Maximum); numItemX.Value = Math.Clamp(config.RepairItemX, numItemX.Minimum, numItemX.Maximum); numItemY.Value = Math.Clamp(config.RepairItemY, numItemY.Minimum, numItemY.Maximum); chkAutoMedicine.Checked = config.AutoBeginnerMedicineEnabled; numMedX.Value = Math.Clamp(config.MedicineItemX, numMedX.Minimum, numMedX.Maximum); numMedY.Value = Math.Clamp(config.MedicineItemY, numMedY.Minimum, numMedY.Maximum); chkMatchNpcText.Checked = config.NpcDialogMatchByText; txtNpcKeywords.Text = config.RepairMenuKeywords; numNpcFallback.Value = Math.Clamp(config.NpcDialogFallbackOption, numNpcFallback.Minimum, numNpcFallback.Maximum); chkAutoTravelBack.Checked = config.AutoTravelBackEnabled; txtTravelKeys.Text = config.TravelMenuKeys; numTravelX.Value = Math.Clamp(config.TravelItemX, numTravelX.Minimum, numTravelX.Maximum); numTravelY.Value = Math.Clamp(config.TravelItemY, numTravelY.Minimum, numTravelY.Maximum); chkAutoSaveRespawn.Checked = config.AutoSaveRespawnEnabled; if (config.TravelMenuKeys.StartsWith("2,5")) cboSpotPreset.SelectedIndex = 0; else if (config.TravelMenuKeys.StartsWith("2,7")) cboSpotPreset.SelectedIndex = 1; else cboSpotPreset.SelectedIndex = 2; UpdateRepairModeUi(); UpdateSupportButtonBadge(config); } finally { _isBindingUi = false; } } private void SyncUiToSessionConfig(BotConfig config) { // 1. Luyện Công config.AutoAttackEnabled = chkAutoAttack.Checked; config.AttackMode = radAttackLeft.Checked ? 1 : 0; if (cboMainAttackSkill.SelectedItem != null && !cboMainAttackSkill.SelectedItem.ToString()!.StartsWith("(")) { config.MainAttackSkillName = cboMainAttackSkill.SelectedItem.ToString()!; } config.AttackIntervalMs = (int)numAttackInterval.Value; config.AttackRadius = cboAttackRadius.SelectedIndex switch { 1 => 200, 2 => 400, 3 => 800, _ => 0 }; config.FollowLeaderEnabled = chkFollowLeader.Checked; config.AutoHorseEnabled = chkAutoHorse.Checked; config.AutoLootEnabled = chkAutoLoot.Checked; config.Buff1Enabled = chkBuff1.Checked; config.Buff1SkillName = cboBuff1Skill.Text.StartsWith("(") ? "" : cboBuff1Skill.Text; if (cboBuff1Key.SelectedItem is KeyOption b1) config.Buff1Key = b1.VirtualKey; config.Buff1IntervalSeconds = (int)numBuff1Sec.Value; config.Buff2Enabled = chkBuff2.Checked; config.Buff2SkillName = cboBuff2Skill.Text.StartsWith("(") ? "" : cboBuff2Skill.Text; if (cboBuff2Key.SelectedItem is KeyOption b2) config.Buff2Key = b2.VirtualKey; config.Buff2IntervalSeconds = (int)numBuff2Sec.Value; config.Buff3Enabled = chkBuff3.Checked; config.Buff3SkillName = cboBuff3Skill.Text.StartsWith("(") ? "" : cboBuff3Skill.Text; if (cboBuff3Key.SelectedItem is KeyOption b3) config.Buff3Key = b3.VirtualKey; config.Buff3IntervalSeconds = (int)numBuff3Sec.Value; config.DetectedSect = cboSect.SelectedItem?.ToString() ?? "Cái Bang"; // 2. Phục Hồi config.HpPump1Enabled = chkHp1.Checked; if (cboHpKey1.SelectedItem is KeyOption hp1) config.HpPump1Key = hp1.VirtualKey; config.HpPump1DelayMs = (int)numHpDelay1.Value; config.HpPump2Enabled = chkHp2.Checked; if (cboHpKey2.SelectedItem is KeyOption hp2) config.HpPump2Key = hp2.VirtualKey; config.HpPump2DelayMs = (int)numHpDelay2.Value; config.MpPump1Enabled = chkMp1.Checked; if (cboMpKey1.SelectedItem is KeyOption mp1) config.MpPump1Key = mp1.VirtualKey; config.MpPump1DelayMs = (int)numMpDelay1.Value; config.MpPump2Enabled = chkMp2.Checked; if (cboMpKey2.SelectedItem is KeyOption mp2) config.MpPump2Key = mp2.VirtualKey; config.MpPump2DelayMs = (int)numMpDelay2.Value; config.AlarmOnTdp = chkAlarmTdp.Checked; config.AutoNewbiePouchEnabled = chkNewbiePouch.Checked; config.NewbiePouchIntervalSeconds = (int)numNewbiePouchSec.Value; // 3. Điều Khiển config.AutoPartyEnabled = chkAutoParty.Checked; config.FollowLeaderEnabled = chkFollowLeader.Checked || chkFollowLeaderTab.Checked; if (!string.IsNullOrWhiteSpace(txtLeaderName.Text)) config.LeaderName = txtLeaderName.Text.Trim(); config.AutoFollowCrossMap = chkFollowCrossMap.Checked; config.AutoTtlEnabled = chkAutoTtl.Checked; config.TtlIntervalMinutes = (int)numTtlInterval.Value; config.ScheduledExitGameEnabled = chkScheduledExit.Checked; config.ExitGameMinutes = (int)numExitMin.Value; config.ScheduledShutdownEnabled = chkScheduledShutdown.Checked; config.ShutdownMinutes = (int)numShutdownMin.Value; // 4. Tọa Độ config.PatrolEnabled = chkPatrol.Checked; config.PatrolIntervalSeconds = (int)numPatrolSec.Value; config.WaypointsData = GetWaypointsDataFromListView(); // 5. Hậu Cần config.PeriodicReturnEnabled = chkPeriodic.Checked; config.ReturnIntervalMinutes = (int)numInterval.Value; if (cboKeyPhu.SelectedItem is KeyOption phu) config.KeyPhuVirtualCode = phu.VirtualKey; config.RepairMode = radBlacksmith.Checked ? 1 : 0; config.BlacksmithNpcX = (int)numBsX.Value; config.BlacksmithNpcY = (int)numBsY.Value; config.RepairItemX = (int)numItemX.Value; config.RepairItemY = (int)numItemY.Value; config.AutoBeginnerMedicineEnabled = chkAutoMedicine.Checked; config.MedicineItemX = (int)numMedX.Value; config.MedicineItemY = (int)numMedY.Value; config.NpcDialogMatchByText = chkMatchNpcText.Checked; config.RepairMenuKeywords = txtNpcKeywords.Text; config.NpcDialogFallbackOption = (int)numNpcFallback.Value; config.AutoTravelBackEnabled = chkAutoTravelBack.Checked; config.TravelMenuKeys = txtTravelKeys.Text; config.TravelItemX = (int)numTravelX.Value; config.TravelItemY = (int)numTravelY.Value; config.AutoSaveRespawnEnabled = chkAutoSaveRespawn.Checked; } private void SyncUiToCurrentSession() { if (_coordinator.ActiveSession == null) return; SyncUiToSessionConfig(_coordinator.ActiveSession.Config); if (chkApplyAll.Checked) { _coordinator.ApplyConfigToAllChecked(_coordinator.ActiveSession.Config); } _coordinator.ActiveSession.Config.SaveForCharacter(_coordinator.ActiveSession.CharacterName); } private void OnBuffSkillChanged(ComboBox cboSkill, NumericUpDown numSec) { if (_isBindingUi) return; string skillName = cboSkill.Text; if (string.IsNullOrWhiteSpace(skillName) || skillName.StartsWith("(")) return; var def = SkillDatabase.FindByName(skillName); if (def != null && def.DefaultCooldownSec > 0) { numSec.Value = Math.Clamp(def.DefaultCooldownSec, numSec.Minimum, numSec.Maximum); AppendLog($"[CHIÊU THỨC] Chọn '{def.Name}' - Thời gian hồi đề xuất: {def.DefaultCooldownSec}s"); } SyncUiToCurrentSession(); } private void OnMainAttackSkillChanged() { if (_isBindingUi) return; string skillName = cboMainAttackSkill.Text; if (string.IsNullOrWhiteSpace(skillName) || skillName.StartsWith("(")) return; AppendLog($"[CHIÊU THỨC] Chọn chiêu công kích: '{skillName}'"); SyncUiToCurrentSession(); } public void PopulateAttackSkillCombo(ComboBox cbo, CharacterSkillProfile profile, string currentVal = "") { cbo.Items.Clear(); cbo.Items.Add("(Chọn chiêu...)"); // 1. Nạp danh sách các chiêu công kích của môn phái từ Bảng Kỹ Năng (hiển thị cấp yêu cầu) if (!string.IsNullOrEmpty(profile.Sect)) { foreach (var def in SkillDatabase.GetAttackSkillsBySect(profile.Sect)) { string displayName = def.ToString(); if (!cbo.Items.Contains(displayName)) { cbo.Items.Add(displayName); } } } // 2. Chọn chiêu thức hiện tại hoặc chiêu thức đề xuất từ Bảng Kỹ Năng (Kháng Long Hữu Hối cho Cái Bang) string target = !string.IsNullOrEmpty(currentVal) && !currentVal.StartsWith("(") ? currentVal : (!string.IsNullOrEmpty(profile.RecommendedAttackSkill) ? profile.RecommendedAttackSkill : ""); object? matched = null; if (!string.IsNullOrEmpty(target)) { matched = cbo.Items.Cast() .Where(item => !item.ToString()!.StartsWith("(")) .FirstOrDefault(item => { string str = item.ToString()!; string baseName = str.Contains('(') ? str.Substring(0, str.IndexOf('(')).Trim() : str; return string.Equals(str, target, StringComparison.OrdinalIgnoreCase) || string.Equals(baseName, target, StringComparison.OrdinalIgnoreCase) || str.StartsWith(target, StringComparison.OrdinalIgnoreCase) || target.StartsWith(baseName, StringComparison.OrdinalIgnoreCase); }); } if (matched != null) { cbo.SelectedItem = matched; } else if (cbo.Items.Count > 1) { cbo.SelectedIndex = 1; } else { cbo.SelectedIndex = 0; } try { System.IO.File.AppendAllText(@"c:\Projects\JX\logs\skill_combo_debug.log", $"[{DateTime.Now}] PopulateAttackSkillCombo: Sect='{profile.Sect}', target='{target}', itemsCount={cbo.Items.Count}, matched='{matched}', selected='{cbo.SelectedItem}', selectedIndex={cbo.SelectedIndex}\n"); } catch {} } private void PopulateBuffSkillCombo(ComboBox cbo, CharacterSkillProfile profile, string currentVal = "", int defaultSkillIndex = 0) { cbo.Items.Clear(); cbo.Items.Add("(Chọn chiêu...)"); // 1. Chỉ nạp các chiêu buff mà nhân vật thực sự sở hữu (từ file .cfg) foreach (var sk in profile.BuffSkills) { if (!cbo.Items.Contains(sk.Name)) { cbo.Items.Add(sk.Name); } } // 2. Chỉ nạp thêm các chiêu buff thuộc ĐÚNG MÔN PHÁI của nhân vật (loại trừ 100% chiêu ngoại phái / đồng đội buff) if (!string.IsNullOrEmpty(profile.Sect)) { foreach (var def in SkillDatabase.GetSkillsBySect(profile.Sect).Where(s => s.IsBuff)) { if (!cbo.Items.Contains(def.Name)) { cbo.Items.Add(def.Name); } } } // 3. Chọn giá trị hợp lệ if (!string.IsNullOrEmpty(currentVal) && cbo.Items.Contains(currentVal) && !currentVal.StartsWith("(")) { cbo.SelectedItem = currentVal; } else if (profile.BuffSkills.Count > defaultSkillIndex) { cbo.SelectedItem = profile.BuffSkills[defaultSkillIndex].Name; } else { cbo.SelectedIndex = 0; } } private void ScanSkillsForCurrentSession() { var session = _coordinator.ActiveSession; string? charName = session != null && !string.IsNullOrEmpty(session.CharacterName) ? session.CharacterName : (session != null && !string.IsNullOrEmpty(session.Config.CharacterName) ? session.Config.CharacterName : null); if (string.IsNullOrWhiteSpace(charName) || charName == "Vo Lam Truyen Ky") charName = "BoCuaCacCon"; string? currentSect = cboSect.SelectedItem?.ToString(); var profile = SkillScanner.ScanCharacterSkills(charName, currentSect); _isBindingUi = true; try { if (cboSect.Items.Contains(profile.Sect)) { cboSect.SelectedItem = profile.Sect; } if (session != null) { session.Config.DetectedSect = profile.Sect; } // Cập nhật nhãn chiêu bị động if (profile.PassiveSkills.Count > 0) { string pNames = string.Join(", ", profile.PassiveSkills.Select(p => $"{p.Name} ({p.Level})")); lblPassiveSkills.Text = $"Bị động: {pNames} (Tự kích hoạt)"; } else { lblPassiveSkills.Text = "Bị động: (Chưa học / Không có)"; } PopulateAttackSkillCombo(cboMainAttackSkill, profile, session?.Config.MainAttackSkillName ?? ""); if (session != null && cboMainAttackSkill.SelectedItem != null && !cboMainAttackSkill.SelectedItem.ToString()!.StartsWith("(")) { session.Config.MainAttackSkillName = cboMainAttackSkill.SelectedItem.ToString()!; } PopulateBuffSkillCombo(cboBuff1Skill, profile, "", 0); PopulateBuffSkillCombo(cboBuff2Skill, profile, "", 1); PopulateBuffSkillCombo(cboBuff3Skill, profile, "", 2); if (profile.ActiveBuffSkills.Count > 0) { cboBuff1Skill.SelectedItem = profile.ActiveBuffSkills[0].Name; chkBuff1.Checked = true; numBuff1Sec.Value = Math.Clamp(profile.ActiveBuffSkills[0].CooldownSec, numBuff1Sec.Minimum, numBuff1Sec.Maximum); BindKeyCombo(cboBuff1Key, 0x02, _buffKeyOptions, 0); // Chuột phải } else { chkBuff1.Checked = false; cboBuff1Skill.SelectedIndex = 0; } if (profile.ActiveBuffSkills.Count > 1) { cboBuff2Skill.SelectedItem = profile.ActiveBuffSkills[1].Name; chkBuff2.Checked = true; numBuff2Sec.Value = Math.Clamp(profile.ActiveBuffSkills[1].CooldownSec, numBuff2Sec.Minimum, numBuff2Sec.Maximum); BindKeyCombo(cboBuff2Key, 0x02, _buffKeyOptions, 0); // Chuột phải } else { chkBuff2.Checked = false; cboBuff2Skill.SelectedIndex = 0; } if (profile.ActiveBuffSkills.Count > 2) { cboBuff3Skill.SelectedItem = profile.ActiveBuffSkills[2].Name; chkBuff3.Checked = true; numBuff3Sec.Value = Math.Clamp(profile.ActiveBuffSkills[2].CooldownSec, numBuff3Sec.Minimum, numBuff3Sec.Maximum); BindKeyCombo(cboBuff3Key, 0x02, _buffKeyOptions, 0); // Chuột phải } else { chkBuff3.Checked = false; cboBuff3Skill.SelectedIndex = 0; numBuff3Sec.Value = 60; BindKeyCombo(cboBuff3Key, 0x02, _buffKeyOptions, 0); // Chuột phải } } finally { _isBindingUi = false; } AppendLog($"[QUÉT CHIÊU] Nhận diện nhân vật '{profile.CharacterName}' ({profile.Sect}) - Tìm thấy {profile.AllSkills.Count} chiêu thức."); AppendLog($"[QUÉT CHIÊU] Buff chủ động: {(profile.ActiveBuffSkills.Count > 0 ? string.Join(", ", profile.ActiveBuffSkills.Select(b => $"{b.Name} ({b.CooldownSec}s)")) : "Không có")}"); AppendLog($"[QUÉT CHIÊU] Bị động (tự kích hoạt): {(profile.PassiveSkills.Count > 0 ? string.Join(", ", profile.PassiveSkills.Select(p => $"{p.Name} ({p.Level})")) : "Không có")}"); SyncUiToCurrentSession(); } private void OnSectSelectionChanged() { if (_isBindingUi) return; var session = _coordinator.ActiveSession; if (session == null) return; string? selectedSect = cboSect.SelectedItem?.ToString(); if (string.IsNullOrEmpty(selectedSect)) return; session.Config.DetectedSect = selectedSect; string charName = !string.IsNullOrEmpty(session.CharacterName) ? session.CharacterName : (!string.IsNullOrEmpty(session.Config.CharacterName) ? session.Config.CharacterName : "BoCuaCacCon"); var profile = SkillScanner.ScanCharacterSkills(charName, selectedSect); _isBindingUi = true; try { // Cập nhật nhãn chiêu bị động if (profile.PassiveSkills.Count > 0) { string pNames = string.Join(", ", profile.PassiveSkills.Select(p => $"{p.Name} ({p.Level})")); lblPassiveSkills.Text = $"Bị động: {pNames} (Tự kích hoạt)"; } else { lblPassiveSkills.Text = "Bị động: (Chưa học / Không có)"; } PopulateAttackSkillCombo(cboMainAttackSkill, profile, ""); if (cboMainAttackSkill.SelectedItem != null && !cboMainAttackSkill.SelectedItem.ToString()!.StartsWith("(")) { session.Config.MainAttackSkillName = cboMainAttackSkill.SelectedItem.ToString()!; } PopulateBuffSkillCombo(cboBuff1Skill, profile, "", 0); PopulateBuffSkillCombo(cboBuff2Skill, profile, "", 1); PopulateBuffSkillCombo(cboBuff3Skill, profile, "", 2); if (profile.ActiveBuffSkills.Count > 0) { cboBuff1Skill.SelectedItem = profile.ActiveBuffSkills[0].Name; chkBuff1.Checked = true; numBuff1Sec.Value = Math.Clamp(profile.ActiveBuffSkills[0].CooldownSec, numBuff1Sec.Minimum, numBuff1Sec.Maximum); BindKeyCombo(cboBuff1Key, 0x02, _buffKeyOptions, 0); // Chuột phải } else { chkBuff1.Checked = false; cboBuff1Skill.SelectedIndex = 0; } if (profile.ActiveBuffSkills.Count > 1) { cboBuff2Skill.SelectedItem = profile.ActiveBuffSkills[1].Name; chkBuff2.Checked = true; numBuff2Sec.Value = Math.Clamp(profile.ActiveBuffSkills[1].CooldownSec, numBuff2Sec.Minimum, numBuff2Sec.Maximum); BindKeyCombo(cboBuff2Key, 0x02, _buffKeyOptions, 0); // Chuột phải } else { chkBuff2.Checked = false; cboBuff2Skill.SelectedIndex = 0; } if (profile.ActiveBuffSkills.Count > 2) { cboBuff3Skill.SelectedItem = profile.ActiveBuffSkills[2].Name; chkBuff3.Checked = true; numBuff3Sec.Value = Math.Clamp(profile.ActiveBuffSkills[2].CooldownSec, numBuff3Sec.Minimum, numBuff3Sec.Maximum); BindKeyCombo(cboBuff3Key, 0x02, _buffKeyOptions, 0); // Chuột phải } else { chkBuff3.Checked = false; cboBuff3Skill.SelectedIndex = 0; } } finally { _isBindingUi = false; } AppendLog($"[MÔN PHÁI] Đã chọn môn phái '{selectedSect}' cho nhân vật '{charName}'"); UpdateSupportButtonBadge(session.Config); SyncUiToCurrentSession(); } private void RegisterEventHandlers() { btnRefreshWindows.Click += (_, _) => RefreshAccountsList(); btnAccountManager.Click += (_, _) => OpenAccountManager(); btnAutoLoginCurrentTab.Click += async (_, _) => await HandleAutoLoginCurrentTabAsync(); var loginMenu = new ContextMenuStrip(); loginMenu.Items.Add("⚡ Smart Login (Log bù hoặc mở mới)", null, async (_, _) => await HandleAutoLoginCurrentTabAsync(false)); loginMenu.Items.Add("⚡ Smart Batch Login (Log bù & mở tất cả)", null, async (_, _) => await HandleAutoLoginCurrentTabAsync(true)); loginMenu.Items.Add(new ToolStripSeparator()); loginMenu.Items.Add("📋 Đối soát trạng thái tài khoản & cửa sổ", null, (_, _) => ShowReconciliationSummary()); btnAutoLoginCurrentTab.ContextMenuStrip = loginMenu; var loginToolTip = new ToolTip(); loginToolTip.SetToolTip(btnAutoLoginCurrentTab, "1-Click Smart Login: Tự nhận diện tài khoản đã lưu, tự quét PID rỗi để log bù hoặc tự mở client mới (Chuột phải để xem menu)"); lvwAccounts.SelectedIndexChanged += (_, _) => OnAccountSelectionChanged(); lvwAccounts.ItemCheck += (_, e) => OnAccountItemCheck(e); lvwAccounts.AfterLabelEdit += OnAccountAfterLabelEdit; lvwAccounts.DoubleClick += OnAccountDoubleClick; btnSaveConfig.Click += (_, _) => { SyncUiToCurrentSession(); AppendLog("[HỆ THỐNG] Đã lưu cấu hình SSoT."); }; btnClearLogs.Click += (_, _) => lstLogs.Items.Clear(); btnStart.Click += (_, _) => HandleStart(); btnStop.Click += (_, _) => HandleStop(); btnRunNow.Click += async (_, _) => await HandleRunNowAsync(); radBlacksmith.CheckedChanged += (_, _) => UpdateRepairModeUi(); radToolBag.CheckedChanged += (_, _) => UpdateRepairModeUi(); btnPickBsCoords.Click += async (_, _) => await HandlePickCoordinatesAsync(numBsX, numBsY, btnPickBsCoords, "Thợ Rèn"); btnPickCoords.Click += async (_, _) => await HandlePickCoordinatesAsync(numItemX, numItemY, btnPickCoords, "Túi đồ nghề"); btnPickMedCoords.Click += async (_, _) => await HandlePickCoordinatesAsync(numMedX, numMedY, btnPickMedCoords, "Dược phẩm tân thủ"); btnPickTtlCoords.Click += async (_, _) => await HandlePickTtlCoordinatesAsync(); btnSupportSkills.Click += (_, _) => OpenSupportSkillsDialog(); btnTestAttack.Click += async (_, _) => await HandleTestAttackAsync(); btnTestHp.Click += async (_, _) => await HandleTestHpAsync(); btnTestNewbiePouch.Click += async (_, _) => await HandleTestNewbiePouchAsync(); btnTestFindLeader.Click += (_, _) => HandleTestFindLeader(); chkNewbiePouch.CheckedChanged += (_, _) => { if (!_isBindingUi) SyncUiToCurrentSession(); }; numNewbiePouchSec.ValueChanged += (_, _) => { if (!_isBindingUi) SyncUiToCurrentSession(); }; chkFollowLeaderTab.CheckedChanged += (_, _) => { if (!_isBindingUi) { chkFollowLeader.Checked = chkFollowLeaderTab.Checked; SyncUiToCurrentSession(); } }; chkFollowLeader.CheckedChanged += (_, _) => { if (!_isBindingUi) { chkFollowLeaderTab.Checked = chkFollowLeader.Checked; SyncUiToCurrentSession(); } }; txtLeaderName.TextChanged += (_, _) => { if (!_isBindingUi) SyncUiToCurrentSession(); }; chkFollowCrossMap.CheckedChanged += (_, _) => { if (!_isBindingUi) SyncUiToCurrentSession(); }; btnTestPhu.Click += async (_, _) => await HandleTestPhuAsync(); btnTestRepair.Click += async (_, _) => await HandleTestRepairAsync(); chkMatchNpcText.CheckedChanged += (_, _) => { if (!_isBindingUi) SyncUiToCurrentSession(); }; txtNpcKeywords.TextChanged += (_, _) => { if (!_isBindingUi) SyncUiToCurrentSession(); }; numNpcFallback.ValueChanged += (_, _) => { if (!_isBindingUi) SyncUiToCurrentSession(); }; chkAutoTravelBack.CheckedChanged += (_, _) => { if (!_isBindingUi) SyncUiToCurrentSession(); }; txtTravelKeys.TextChanged += (_, _) => { if (!_isBindingUi) SyncUiToCurrentSession(); }; numTravelX.ValueChanged += (_, _) => { if (!_isBindingUi) SyncUiToCurrentSession(); }; numTravelY.ValueChanged += (_, _) => { if (!_isBindingUi) SyncUiToCurrentSession(); }; btnPickTravelCoords.Click += async (_, _) => await HandlePickCoordinatesAsync(numTravelX, numTravelY, btnPickTravelCoords, "Thần Hành Phù"); btnTestTravel.Click += async (_, _) => await HandleTestTravelAsync(); chkAutoSaveRespawn.CheckedChanged += (_, _) => { if (!_isBindingUi) SyncUiToCurrentSession(); }; btnTestSaveRespawn.Click += async (_, _) => await HandleTestSaveRespawnAsync(); cboSpotPreset.SelectedIndexChanged += (_, _) => OnSpotPresetChanged(); // Nút Ẩn / Hiện Game btnHideGame.Click += (_, _) => { var hwnd = GetSelectedHwnd(); if (hwnd != IntPtr.Zero) { ControlEngine.HideGame(hwnd); AppendLog("[Đ.KHIỂN] Đã ẩn cửa sổ game xuống nền."); } }; btnShowGame.Click += (_, _) => { var hwnd = GetSelectedHwnd(); if (hwnd != IntPtr.Zero) { ControlEngine.ShowGame(hwnd); AppendLog("[Đ.KHIỂN] Đã hiện lại cửa sổ game."); } }; // Nút Tọa Độ Tuần Tra btnAddCurrentCoord.Click += async (_, _) => await HandleAddCurrentCoordAsync(); btnRemoveCoord.Click += (_, _) => HandleRemoveCoord(); btnClearCoords.Click += (_, _) => { lvwWaypoints.Items.Clear(); AppendLog("[T.ĐỘ] Đã xóa toàn bộ lộ trình tuần tra."); }; BackgroundInput.OnInputWarning += AppendLog; _coordinator.OnSessionsChanged += () => { if (InvokeRequired) Invoke(PopulateAccountsListView); else PopulateAccountsListView(); }; _coordinator.OnSessionUpdated += session => { if (InvokeRequired) Invoke(UpdateMasterStatus); else UpdateMasterStatus(); }; _coordinator.OnSessionLog += (session, msg) => { AppendLog($"[{session.WindowInfo.ProcessName}:{session.WindowInfo.ProcessId}] {msg}"); }; } private void RefreshAccountsList() { if (_coordinator.ActiveSession != null && !_isBindingUi) { SyncUiToSessionConfig(_coordinator.ActiveSession.Config); _coordinator.ActiveSession.Config.SaveForCharacter(_coordinator.ActiveSession.CharacterName); } _coordinator.SyncWindows(_baseConfig); PopulateAccountsListView(); } private void PopulateAccountsListView() { lvwAccounts.BeginUpdate(); lvwAccounts.Items.Clear(); foreach (var session in _coordinator.Sessions) { var item = new ListViewItem(session.WindowInfo.DisplayName) { Tag = session, Checked = session.IsChecked }; if (session.WindowInfo.IsIdle) { item.ForeColor = Color.FromArgb(190, 80, 0); item.SubItems.Add("Chờ log bù"); } else { item.ForeColor = Color.FromArgb(0, 100, 0); item.SubItems.Add(session.StatusText); } lvwAccounts.Items.Add(item); if (_coordinator.ActiveSession == session) { item.Selected = true; } } if (lvwAccounts.SelectedItems.Count == 0 && lvwAccounts.Items.Count > 0) { lvwAccounts.Items[0].Selected = true; } lvwAccounts.EndUpdate(); UpdateMasterStatus(); if (_coordinator.ActiveSession != null) { BindSessionConfigToUi(_coordinator.ActiveSession.Config); } } private void OnAccountSelectionChanged() { if (lvwAccounts.SelectedItems.Count == 0) return; var selectedItem = lvwAccounts.SelectedItems[0]; if (selectedItem.Tag is BotSession newSession) { if (_coordinator.ActiveSession != null && _coordinator.ActiveSession != newSession && !_isBindingUi) { SyncUiToSessionConfig(_coordinator.ActiveSession.Config); _coordinator.ActiveSession.Config.SaveForCharacter(_coordinator.ActiveSession.CharacterName); } _coordinator.ActiveSession = newSession; if (!string.IsNullOrEmpty(newSession.CharacterName)) { newSession.Config.CharacterName = newSession.CharacterName; newSession.Config.LastSelectedTitle = newSession.CharacterName; } BindSessionConfigToUi(newSession.Config); UpdateMasterStatus(); } } private void OnAccountAfterLabelEdit(object? sender, LabelEditEventArgs e) { if (string.IsNullOrWhiteSpace(e.Label)) { e.CancelEdit = true; return; } if (e.Item >= 0 && e.Item < lvwAccounts.Items.Count) { var item = lvwAccounts.Items[e.Item]; if (item.Tag is BotSession session) { string newName = e.Label.Trim(); session.WindowInfo = session.WindowInfo with { CharacterName = newName }; session.Config.CharacterName = newName; session.Config.LastSelectedTitle = newName; session.Config.SaveForCharacter(newName); AppendLog($"[ĐỔI TÊN] Đã đổi tên nhân vật thành '{newName}'"); ScanSkillsForCurrentSession(); } } } private void OnAccountDoubleClick(object? sender, EventArgs e) { if (lvwAccounts.SelectedItems.Count == 0) return; var selected = lvwAccounts.SelectedItems[0]; if (selected.Tag is BotSession session) { WindowManager.RestoreIfMinimized(session.WindowInfo.Handle); NativeMethods.SetForegroundWindow(session.WindowInfo.Handle); AppendLog($"[KÍCH HOẠT] Đã đưa cửa sổ '{session.CharacterName}' lên trước"); } } private void OnAccountItemCheck(ItemCheckEventArgs e) { if (e.Index >= 0 && e.Index < lvwAccounts.Items.Count) { var item = lvwAccounts.Items[e.Index]; if (item.Tag is BotSession session) { session.IsChecked = (e.NewValue == CheckState.Checked); } } } private void UpdateMasterStatus() { int total = _coordinator.Sessions.Count; int running = _coordinator.GetRunningCount(); lblStatus.Text = $"Tổng: {total} tab | Chạy: {running}"; btnStart.Enabled = (running == 0 || running < total); btnStop.Enabled = (running > 0); foreach (ListViewItem item in lvwAccounts.Items) { if (item.Tag is BotSession s) { if (item.Text != s.WindowInfo.DisplayName) { item.Text = s.WindowInfo.DisplayName; } if (item.SubItems.Count > 1 && item.SubItems[1].Text != s.StatusText) { item.SubItems[1].Text = s.StatusText; } } } if (_coordinator.ActiveSession != null) { lblCountdown.Text = _coordinator.ActiveSession.RemainingTime > TimeSpan.Zero ? $"Kế tiếp: {_coordinator.ActiveSession.RemainingTime:mm\\:ss}" : "Kế tiếp: --:--"; } } private void UpdateRepairModeUi() { bool isBs = radBlacksmith.Checked; numBsX.Enabled = isBs; numBsY.Enabled = isBs; btnPickBsCoords.Enabled = isBs; numItemX.Enabled = !isBs; numItemY.Enabled = !isBs; btnPickCoords.Enabled = !isBs; } private IntPtr GetSelectedHwnd() { return _coordinator.ActiveSession?.WindowInfo.Handle ?? IntPtr.Zero; } private void HandleStart() { if (_coordinator.Sessions.Count == 0) { MessageBox.Show("Không tìm thấy cửa sổ game nào đang mở!", "Thông báo", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } SyncUiToCurrentSession(); _coordinator.StartAllChecked(); UpdateMasterStatus(); AppendLog($"[HỆ THỐNG] Đã kích hoạt các tab game được tích chọn."); } private void HandleStop() { _coordinator.StopAll(); lblCountdown.Text = "Kế tiếp: --:--"; UpdateMasterStatus(); AppendLog("[HỆ THỐNG] Đã dừng toàn bộ các tab game."); } private async Task HandleRunNowAsync() { var session = _coordinator.ActiveSession; if (session == null || session.WindowInfo.Handle == IntPtr.Zero) { MessageBox.Show("Vui lòng chọn một tab game trong danh sách trước!", "Thông báo", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } WindowManager.RestoreIfMinimized(session.WindowInfo.Handle); SyncUiToCurrentSession(); btnRunNow.Enabled = false; await session.Brain.TriggerImmediateCycleAsync(session.WindowInfo.Handle, session.Config); btnRunNow.Enabled = true; } private async Task HandleTestAttackAsync() { var hwnd = GetSelectedHwnd(); if (hwnd == IntPtr.Zero) return; AppendLog("[TEST] Thử xuất chiêu đánh ngầm..."); if (radAttackLeft.Checked) { await BackgroundInput.SendLeftClickAsync(hwnd, 400, 300); } else { await BackgroundInput.SendRightClickAsync(hwnd, 400, 300); } AppendLog("[TEST] Đã gửi lệnh đánh ngầm."); } internal void UpdateSupportButtonBadge(BotConfig config) { string sect = !string.IsNullOrEmpty(config.DetectedSect) ? config.DetectedSect : (cboSect.SelectedItem?.ToString() ?? "Cái Bang"); if (sect.Equals("Nga My", StringComparison.OrdinalIgnoreCase)) { btnSupportSkills.Text = config.NgaMyHealParty ? "⚙ Nga My (Nhóm)" : (config.NgaMyHealEnabled ? "⚙ Nga My (Đơn)" : "⚙ Nga My..."); btnSupportSkills.BackColor = Color.MidnightBlue; } else if (sect.Equals("Ngũ Độc", StringComparison.OrdinalIgnoreCase)) { btnSupportSkills.Text = (config.NguDocCurseChainEnabled || config.CurseRotationEnabled) ? "⚙ Ngũ Độc (Kim Yến)" : "⚙ Ngũ Độc..."; btnSupportSkills.BackColor = Color.DarkGreen; } else { btnSupportSkills.Text = $"⚙ Hỗ trợ ({sect})..."; btnSupportSkills.BackColor = Color.MidnightBlue; } } internal void SelectTab(int index) { if (index >= 0 && index < tabControl.TabPages.Count) { tabControl.SelectedIndex = index; } } private void OpenSupportSkillsDialog() { var session = _coordinator.ActiveSession; BotConfig targetConfig = session != null ? session.Config : _baseConfig; string charName = session != null ? (!string.IsNullOrEmpty(session.CharacterName) ? session.CharacterName : session.Config.CharacterName) : (!string.IsNullOrEmpty(_baseConfig.CharacterName) ? _baseConfig.CharacterName : "BoCuaCacCon"); string sect = !string.IsNullOrEmpty(targetConfig.DetectedSect) ? targetConfig.DetectedSect : (cboSect.SelectedItem?.ToString() ?? "Cái Bang"); using var dlg = new SupportSkillsForm(targetConfig, charName, sect); dlg.OnConfigSaved += updatedConfig => { BindSessionConfigToUi(updatedConfig); UpdateSupportButtonBadge(updatedConfig); updatedConfig.SaveForCharacter(charName); AppendLog($"[HỖ TRỢ] Đã cập nhật chiêu hỗ trợ & phái đặc thù cho '{charName}' ({sect})."); }; dlg.ShowDialog(this); } private void OpenAccountManager() { using var dlg = new AccountManagerForm(); if (dlg.ShowDialog(this) == DialogResult.OK) { RefreshAccountsList(); } } private async Task HandleAutoLoginCurrentTabAsync(bool forceBatchAll = false) { btnAutoLoginCurrentTab.Enabled = false; btnAutoLoginCurrentTab.Text = "⏳..."; try { var accounts = AccountStorage.LoadAccounts(); if (accounts.Count == 0) { AppendLog("[ĐĂNG NHẬP] Chưa có tài khoản nào trong hệ thống. Vui lòng nhấn '👤Acc' để thêm!"); return; } if (forceBatchAll) { AppendLog("[SMART BATCH] Khởi chạy chu trình đối soát và tự động đăng nhập toàn bộ tài khoản..."); int processed = await AutoLoginEngine.SmartReconcileAllAccountsAsync(msg => AppendLog($"[Đ.NHẬP] {msg}")); AppendLog($"[SMART BATCH] Hoàn tất đối soát! Đã xử lý thành công {processed} tài khoản."); await Task.Delay(1000); RefreshAccountsList(); return; } // 1. Kiểm tra tab đang chọn var session = _coordinator.ActiveSession; AccountEntry? targetAccount = null; // Nếu người dùng đang chọn một tab và tab đó là CỬA SỔ RỖI (chưa đăng nhập nhân vật) if (session != null && session.WindowInfo.IsIdle) { var reconcile = AutoLoginEngine.ReconcileAccounts(accounts); targetAccount = reconcile.AccountStatuses.FirstOrDefault(s => !s.IsOnline && s.Account.IsEnabled)?.Account ?? accounts[0]; AppendLog($"[SMART LOGIN] Nhận diện tab rỗi PID {session.WindowInfo.ProcessId} (HWND 0x{session.WindowInfo.Handle.ToInt64():X8}) -> Đang log bù '{targetAccount.CharacterName}' ({targetAccount.Username})..."); WindowManager.RestoreIfMinimized(session.WindowInfo.Handle); bool ok = await AutoLoginEngine.ExecuteAutoLoginAsync(session.WindowInfo.Handle, targetAccount, msg => AppendLog($"[Đ.NHẬP] {msg}")); if (ok) { AppendLog($"[ĐĂNG NHẬP THÀNH CÔNG] Đã log bù thành công cho '{targetAccount.CharacterName}'!"); await Task.Delay(1000); RefreshAccountsList(); } else { AppendLog($"[ĐĂNG NHẬP THẤT BẠI] Không thể hoàn tất log bù cho '{targetAccount.CharacterName}'."); } return; } // 2. Nếu tab đang chọn đã có nhân vật HOẶC không có tab nào được chọn: // Tự động đối soát toàn bộ: tìm tài khoản chưa online để log bù vào cửa sổ rỗi hoặc mở mới client! var report = AutoLoginEngine.ReconcileAccounts(accounts); var nextAccount = report.AccountStatuses.FirstOrDefault(s => !s.IsOnline && s.Account.IsEnabled)?.Account; if (nextAccount == null) { AppendLog($"[SMART LOGIN] Tất cả tài khoản ({report.TotalOnline}/{accounts.Count}) đã online đầy đủ trong game! Không cần đăng nhập thêm."); return; } if (report.IdleWindows.Count > 0) { var idleWin = report.IdleWindows[0]; AppendLog($"[SMART LOGIN] Phát hiện cửa sổ rỗi PID {idleWin.ProcessId} -> Đang tự động log bù '{nextAccount.CharacterName}' ({nextAccount.Username})..."); bool ok = await AutoLoginEngine.SmartLoginAccountAsync(nextAccount, msg => AppendLog($"[Đ.NHẬP] {msg}")); if (ok) { AppendLog($"[ĐĂNG NHẬP THÀNH CÔNG] Đã log bù thành công cho '{nextAccount.CharacterName}'!"); await Task.Delay(1000); RefreshAccountsList(); } else { AppendLog($"[ĐĂNG NHẬP THẤT BẠI] Không thể log bù cho '{nextAccount.CharacterName}'."); } } else { AppendLog($"[SMART LOGIN] Không có cửa sổ trống -> Đang tự động khởi chạy client mới cho '{nextAccount.CharacterName}' ({nextAccount.Username})..."); bool ok = await AutoLoginEngine.SmartLoginAccountAsync(nextAccount, msg => AppendLog($"[Đ.NHẬP] {msg}")); if (ok) { AppendLog($"[ĐĂNG NHẬP THÀNH CÔNG] Đã khởi chạy và đăng nhập thành công cho '{nextAccount.CharacterName}'!"); await Task.Delay(1000); RefreshAccountsList(); } else { AppendLog($"[ĐĂNG NHẬP THẤT BẠI] Không thể đăng nhập cho '{nextAccount.CharacterName}'."); } } } catch (Exception ex) { AppendLog($"[ĐĂNG NHẬP LỖI] Ngoại lệ: {ex.Message}"); } finally { btnAutoLoginCurrentTab.Enabled = true; btnAutoLoginCurrentTab.Text = "⚡Login"; } } private void ShowReconciliationSummary() { var accounts = AccountStorage.LoadAccounts(); var report = AutoLoginEngine.ReconcileAccounts(accounts); AppendLog($"[ĐỐI SOÁT] Tổng số tài khoản đã lưu: {accounts.Count}"); AppendLog($"[ĐỐI SOÁT] Tài khoản đã online trong game ({report.TotalOnline}):"); foreach (var s in report.AccountStatuses.Where(x => x.IsOnline)) { AppendLog($" 🟢 {s.Account.CharacterName} ({s.Account.Username}) -> PID: {s.WindowInfo?.ProcessId}"); } AppendLog($"[ĐỐI SOÁT] Tài khoản chưa đăng nhập ({report.TotalPending}):"); foreach (var s in report.AccountStatuses.Where(x => !x.IsOnline && x.Account.IsEnabled)) { AppendLog($" ⚪ {s.Account.CharacterName} ({s.Account.Username})"); } AppendLog($"[ĐỐI SOÁT] Cửa sổ game rỗi chưa log nhân vật ({report.IdleWindows.Count}):"); foreach (var w in report.IdleWindows) { AppendLog($" 🟠 Cửa sổ rỗi PID: {w.ProcessId} (HWND 0x{w.Handle.ToInt64():X8}) - '{w.Title}'"); } } private async Task HandleTestHpAsync() { var hwnd = GetSelectedHwnd(); if (hwnd == IntPtr.Zero) return; if (cboHpKey1.SelectedItem is KeyOption hp) { AppendLog($"[TEST] Thử gửi phím bơm máu ngầm ({hp.DisplayName})..."); await BackgroundInput.SendKeyAsync(hwnd, hp.VirtualKey); AppendLog("[TEST] Đã gửi phím bơm máu ngầm."); } } private async Task HandleTestNewbiePouchAsync() { var hwnd = GetSelectedHwnd(); if (hwnd == IntPtr.Zero) { AppendLog("[TÚI MÁU] Vui lòng chọn một cửa sổ game để thử nghiệm."); return; } var session = _coordinator.ActiveSession; var config = session?.Config ?? _baseConfig; AppendLog($"[TÚI MÁU] Thử nghiệm mở Túi Máu Tân Thủ tại ({config.NewbiePouchItemX}, {config.NewbiePouchItemY})..."); btnTestNewbiePouch.Enabled = false; try { using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); bool ok = await ActionExecutor.ExecuteUseNewbiePouchAsync(hwnd, config, AppendLog, cts.Token); if (ok) AppendLog("[TÚI MÁU] Thử nghiệm thành công!"); else AppendLog("[TÚI MÁU] Thử nghiệm không thành công."); } finally { btnTestNewbiePouch.Enabled = true; } } private void HandleTestFindLeader() { string leaderName = txtLeaderName.Text.Trim(); if (string.IsNullOrWhiteSpace(leaderName)) { AppendLog("[BÁM ACC CHÍNH] Chưa nhập tên Acc Chính."); return; } AppendLog($"[BÁM ACC CHÍNH] Đang tra cứu vị trí thời gian thực của '{leaderName}'..."); var stats = PlayerStatsReader.FindCharacterLiveStats(leaderName); if (stats != null) { AppendLog($"[BÁM ACC CHÍNH] Tìm thấy Acc Chính '{stats.CharacterName}'! Map: {stats.MapName} (ID {stats.MapId}) | Tọa độ HUD: ({stats.HudX}, {stats.HudY}) | HP: {stats.CurrentHp}/{stats.MaxHp}"); } else { AppendLog($"[BÁM ACC CHÍNH] Không tìm thấy dữ liệu RAM của '{leaderName}'. Vui lòng kiểm tra cửa sổ game đã vào nhân vật chưa."); } } private async Task HandleTestPhuAsync() { var hwnd = GetSelectedHwnd(); if (hwnd == IntPtr.Zero) return; if (cboKeyPhu.SelectedItem is KeyOption phu) { AppendLog($"[TEST] Gửi phím Phù ngầm ({phu.DisplayName})..."); await BackgroundInput.SendKeyAsync(hwnd, phu.VirtualKey); if (chkAlarmTdp.Checked) RecoveryEngine.PlayTdpAlarm(); AppendLog("[TEST] Đã gửi lệnh phím Phù."); } } private async Task HandleTestRepairAsync() { var session = _coordinator.ActiveSession; if (session == null || session.WindowInfo.Handle == IntPtr.Zero) return; SyncUiToCurrentSession(); btnTestRepair.Enabled = false; try { await ActionExecutor.ExecuteSingleRepairAsync(session.WindowInfo.Handle, session.Config, AppendLog); } finally { btnTestRepair.Enabled = true; } } private async Task HandleTestTravelAsync() { var session = _coordinator.ActiveSession; if (session == null || session.WindowInfo.Handle == IntPtr.Zero) return; SyncUiToCurrentSession(); btnTestTravel.Enabled = false; try { await ActionExecutor.ExecuteSingleTravelBackAsync(session.WindowInfo.Handle, session.Config, AppendLog); } finally { btnTestTravel.Enabled = true; } } private void OnSpotPresetChanged() { if (_isBindingUi) return; if (cboSpotPreset.SelectedIndex == 0) // Lão Hổ Động (5x) { txtTravelKeys.Text = "2,5,4"; AppendLog("[BÃI TRAIN] Đã chọn cấu hình Lão Hổ Động (Cấp 5x) -> Phím THP: 2,5,4"); } else if (cboSpotPreset.SelectedIndex == 1) // Nghiệt Long Động (7x) { txtTravelKeys.Text = "2,7,1"; AppendLog("[BÃI TRAIN] Đã chọn cấu hình Nghiệt Long Động (Cấp 7x) -> Phím THP: 2,7,1"); } SyncUiToCurrentSession(); } private async Task HandleTestSaveRespawnAsync() { var session = _coordinator.ActiveSession; if (session == null || session.WindowInfo.Handle == IntPtr.Zero) return; SyncUiToCurrentSession(); btnTestSaveRespawn.Enabled = false; try { await ActionExecutor.ExecuteSaveRespawnAsync(session.WindowInfo.Handle, session.Config, AppendLog); } finally { btnTestSaveRespawn.Enabled = true; } } private async Task HandlePickCoordinatesAsync(NumericUpDown targetX, NumericUpDown targetY, Button btn, string targetName) { var hwnd = GetSelectedHwnd(); if (hwnd == IntPtr.Zero) return; btn.Enabled = false; string origText = btn.Text; for (int sec = 3; sec >= 1; sec--) { btn.Text = $"Chỉ: {sec}s"; AppendLog($"[TỌA ĐỘ] Di chuột vào {targetName}... ({sec}s)"); await Task.Delay(1000); } var pt = WindowManager.GetRelativeCursorPoint(hwnd); if (pt.HasValue) { targetX.Value = Math.Clamp(pt.Value.X, targetX.Minimum, targetX.Maximum); targetY.Value = Math.Clamp(pt.Value.Y, targetY.Minimum, targetY.Maximum); System.Media.SystemSounds.Beep.Play(); AppendLog($"[TỌA ĐỘ] ĐÃ LẤY THÀNH CÔNG: X={pt.Value.X}, Y={pt.Value.Y}"); } btn.Text = origText; btn.Enabled = true; } private async Task HandlePickTtlCoordinatesAsync() { var hwnd = GetSelectedHwnd(); if (hwnd == IntPtr.Zero || _coordinator.ActiveSession == null) return; btnPickTtlCoords.Enabled = false; string origText = btnPickTtlCoords.Text; for (int sec = 3; sec >= 1; sec--) { btnPickTtlCoords.Text = $"{sec}s"; AppendLog($"[TIỆN ÍCH] Di chuột vào ô Tiên Thảo Lộ trong F4... ({sec}s)"); await Task.Delay(1000); } var pt = WindowManager.GetRelativeCursorPoint(hwnd); if (pt.HasValue) { _coordinator.ActiveSession.Config.TtlItemX = pt.Value.X; _coordinator.ActiveSession.Config.TtlItemY = pt.Value.Y; System.Media.SystemSounds.Beep.Play(); AppendLog($"[TIỆN ÍCH] ĐÃ LẤY TỌA ĐỘ TIÊN THẢO LỘ: X={pt.Value.X}, Y={pt.Value.Y}"); } btnPickTtlCoords.Text = origText; btnPickTtlCoords.Enabled = true; } // Tọa Độ & Tuần Tra Handlers private void PopulateWaypointsList(string? waypointsData) { lvwWaypoints.Items.Clear(); var points = PatrolEngine.ParseWaypoints(waypointsData); for (int i = 0; i < points.Count; i++) { var item = new ListViewItem((i + 1).ToString()) { Tag = points[i] }; item.SubItems.Add($"{points[i].X}, {points[i].Y}"); lvwWaypoints.Items.Add(item); } } private string GetWaypointsDataFromListView() { var points = new List(); foreach (ListViewItem item in lvwWaypoints.Items) { if (item.Tag is Point pt) points.Add(pt); } return PatrolEngine.FormatWaypoints(points); } private async Task HandleAddCurrentCoordAsync() { var hwnd = GetSelectedHwnd(); if (hwnd == IntPtr.Zero) return; btnAddCurrentCoord.Enabled = false; string origText = btnAddCurrentCoord.Text; for (int sec = 3; sec >= 1; sec--) { btnAddCurrentCoord.Text = $"{sec}s"; AppendLog($"[T.ĐỘ] Di chuột đến điểm muốn tuần tra... ({sec}s)"); await Task.Delay(1000); } var pt = WindowManager.GetRelativeCursorPoint(hwnd); if (pt.HasValue) { int index = lvwWaypoints.Items.Count + 1; var item = new ListViewItem(index.ToString()) { Tag = pt.Value }; item.SubItems.Add($"{pt.Value.X}, {pt.Value.Y}"); lvwWaypoints.Items.Add(item); System.Media.SystemSounds.Beep.Play(); AppendLog($"[T.ĐỘ] ĐÃ THÊM WAYPOINT {index}: ({pt.Value.X}, {pt.Value.Y})"); } btnAddCurrentCoord.Text = origText; btnAddCurrentCoord.Enabled = true; } private void HandleRemoveCoord() { if (lvwWaypoints.SelectedItems.Count > 0) { lvwWaypoints.Items.Remove(lvwWaypoints.SelectedItems[0]); // Re-index for (int i = 0; i < lvwWaypoints.Items.Count; i++) { lvwWaypoints.Items[i].Text = (i + 1).ToString(); } AppendLog("[T.ĐỘ] Đã xóa điểm khỏi lộ trình."); } } private void AppendLog(string message) { if (InvokeRequired) { Invoke(() => AppendLog(message)); return; } lstLogs.Items.Add(message); lstLogs.TopIndex = lstLogs.Items.Count - 1; } }