using System;
using System.Collections.Generic;
using System.Linq;
using JXHelper.Common;
using JXHelper.Config;
using JXHelper.Platform;
namespace JXHelper.Core;
///
/// Bộ điều phối đa tab game (Multi-Instance Coordinator).
/// Quản lý danh sách các BotSession độc lập, hỗ trợ Start/Stop hàng loạt và đồng bộ cửa sổ.
///
public class BotCoordinator
{
private readonly List _sessions = new();
private readonly object _lock = new();
public IReadOnlyList Sessions
{
get
{
lock (_lock)
{
return _sessions.ToList();
}
}
}
public BotSession? ActiveSession { get; set; }
public event Action? OnSessionsChanged;
public event Action? OnSessionUpdated;
public event Action? OnSessionLog;
public void SyncWindows(BotConfig? baseConfig = null)
{
lock (_lock)
{
var detected = WindowManager.FindGameWindows();
var detectedHandles = detected.Select(d => d.Handle).ToHashSet();
// 1. Dừng và gỡ bỏ các session có cửa sổ đã đóng
for (int i = _sessions.Count - 1; i >= 0; i--)
{
var s = _sessions[i];
if (!detectedHandles.Contains(s.WindowInfo.Handle) || !NativeMethods.IsWindow(s.WindowInfo.Handle))
{
s.Stop();
_sessions.RemoveAt(i);
}
}
// 2. Thêm mới các cửa sổ game chưa có trong danh sách
foreach (var win in detected)
{
var existing = _sessions.FirstOrDefault(s => s.WindowInfo.Handle == win.Handle);
if (existing != null)
{
// Cập nhật lại tiêu đề / trạng thái thu nhỏ / tên nhân vật nếu có thay đổi
existing.WindowInfo = win;
if (string.IsNullOrEmpty(existing.Config.CharacterName) && !string.IsNullOrEmpty(win.CharacterName))
{
existing.Config.CharacterName = win.CharacterName;
}
}
else
{
// Tải cấu hình độc lập cho từng nhân vật theo tên
var cfg = !string.IsNullOrEmpty(win.CharacterName)
? BotConfig.LoadForCharacter(win.CharacterName)
: (baseConfig != null ? CloneConfig(baseConfig) : BotConfig.Load());
if (!string.IsNullOrEmpty(win.CharacterName))
{
cfg.CharacterName = win.CharacterName;
}
var newSession = new BotSession(win, cfg);
newSession.OnStateUpdated += s => OnSessionUpdated?.Invoke(s);
newSession.OnLogReceived += (s, msg) => OnSessionLog?.Invoke(s, msg);
_sessions.Add(newSession);
}
}
// Đảm bảo luôn có ActiveSession nếu danh sách không rỗng
if (ActiveSession == null || !_sessions.Contains(ActiveSession))
{
ActiveSession = _sessions.FirstOrDefault();
}
}
OnSessionsChanged?.Invoke();
}
public void StartAllChecked()
{
lock (_lock)
{
foreach (var session in _sessions.Where(s => s.IsChecked))
{
session.Start();
}
}
}
public void StopAll()
{
lock (_lock)
{
foreach (var session in _sessions)
{
session.Stop();
}
}
}
///
/// Đồng bộ cấu hình dùng chung (Bơm máu, Tổ đội, Hậu cần, Tuần tra) cho các tab được tích chọn.
/// TUYỆT ĐỐI KHÔNG ghi đè Môn phái và Chiêu thức của từng nhân vật!
///
public void ApplyConfigToAllChecked(BotConfig sourceConfig)
{
lock (_lock)
{
foreach (var session in _sessions.Where(s => s.IsChecked))
{
if (session.Config != sourceConfig)
{
ApplyCommonSettings(sourceConfig, session.Config);
session.Config.SaveForCharacter();
}
}
}
}
public int GetRunningCount()
{
lock (_lock)
{
return _sessions.Count(s => s.IsRunning);
}
}
public BotSession? GetSessionByCharacter(string characterName)
{
lock (_lock)
{
return _sessions.FirstOrDefault(s => string.Equals(s.CharacterName, characterName, StringComparison.OrdinalIgnoreCase));
}
}
///
/// Điều phối chuỗi kịch bản thực địa 4 tài khoản theo yêu cầu chỉ huy:
/// 1. Về thành Dương Châu & Lưu điểm hồi sinh qua Thần Hành Phù.
/// 2. Truyền tống Nghiệt Long Động tuần tự: Cái Bang -> Thiếu Lâm -> Nga My -> Ngũ Độc.
/// 3. Thiết lập Tổ đội: Thiếu Lâm làm chủ PT mời các thành viên, thành viên auto-accept.
/// 4. Đội hình: 3 thành viên theo sau Thiếu Lâm khoảng cách 100px.
/// 5. Tác chiến: Thiếu Lâm đánh quanh điểm bán kính 1000px, các thành viên hỗ trợ & xuất chiêu theo phái.
///
public async Task OrchestrateFieldScenarioAsync(Action? logCallback = null)
{
void Log(string msg)
{
TelemetryLogger.LogAction(msg);
logCallback?.Invoke(msg);
}
Log("[ĐIỀU PHỐI TÁC CHIẾN] Bắt đầu khởi chạy kịch bản thực địa 4 tài khoản...");
var caiBang = GetSessionByCharacter("BoCuaCacCon");
var thieuLam = GetSessionByCharacter("ConCuaBoMe");
var ngaMy = GetSessionByCharacter("MeCuaCacCon");
var nguDoc = GetSessionByCharacter("ConGailCung");
// Giai đoạn 1: Về thành Dương Châu & Lưu điểm hồi sinh
Log("[GIAI ĐOẠN 1] Triệu hồi Thần Hành Phù về thành Dương Châu và Lưu điểm hồi sinh...");
var allSessions = new[] { caiBang, thieuLam, ngaMy, nguDoc }.Where(s => s != null).ToList();
foreach (var session in allSessions)
{
Log($"[DƯƠNG CHÂU] Đang thực thi cho nhân vật '{session!.CharacterName}'...");
var thp = new ThanHanhPhuExecutor(session.WindowInfo.Handle, session.Config);
await thp.ExecuteReturnToDuongChauAsync(saveRespawn: true);
await Task.Delay(1000);
}
// Giai đoạn 2: Truyền tống Nghiệt Long Động tuần tự
// Thứ tự bắt buộc: (1) Cái Bang -> (2) Thiếu Lâm -> (3) Nga My -> (4) Ngũ Độc
Log("[GIAI ĐOẠN 2] Truyền tống Nghiệt Long Động theo thứ tự nghiêm ngặt: Cái Bang -> Thiếu Lâm -> Nga My -> Ngũ Độc...");
var sequence = new[] {
("Cái Bang (BoCuaCacCon)", caiBang),
("Thiếu Lâm (ConCuaBoMe)", thieuLam),
("Nga My (MeCuaCacCon)", ngaMy),
("Ngũ Độc (ConGailCung)", nguDoc)
};
foreach (var (role, session) in sequence)
{
if (session != null)
{
Log($"[NGHIỆT LONG ĐỘNG] Dịch chuyển {role}...");
var thp = new ThanHanhPhuExecutor(session.WindowInfo.Handle, session.Config);
await thp.ExecuteReturnToNghietLongGateAsync();
await Task.Delay(1500);
}
else
{
Log($"[CẢNH BÁO] Không tìm thấy phiên làm việc cho {role}. Bỏ qua bước này.");
}
}
// Giai đoạn 3: Thiết lập Đội hình & Tổ đội (Thiếu Lâm làm chủ PT)
Log("[GIAI ĐOẠN 3] Thiết lập Tổ Đội: Thiếu Lâm (ConCuaBoMe) làm Chủ PT, các thành viên theo sau cự ly 100px...");
if (thieuLam != null)
{
thieuLam.Config.IsPartyLeader = true;
thieuLam.Config.PartyMembers = "BoCuaCacCon,MeCuaCacCon,ConGailCung";
thieuLam.Config.LockCurrentSpot = false;
thieuLam.Config.AttackRadius = 1000;
thieuLam.Config.FollowLeaderEnabled = false;
thieuLam.Config.SaveForCharacter();
}
var followers = new[] { caiBang, ngaMy, nguDoc }.Where(s => s != null);
foreach (var f in followers)
{
f!.Config.IsPartyLeader = false;
f.Config.FollowLeaderEnabled = true;
f.Config.LeaderName = "ConCuaBoMe";
f.Config.FollowDistance = 100;
f.Config.AutoPartyEnabled = true;
f.Config.SaveForCharacter();
}
// Giai đoạn 4: Khởi động Động cơ Chiến đấu & Hỗ trợ
Log("[GIAI ĐOẠN 4] Khởi động tất cả các phiên chiến đấu...");
StartAllChecked();
Log("[ĐIỀU PHỐI TÁC CHIẾN] Hoàn tất điều phối kịch bản thực địa thành công!");
return true;
}
private static BotConfig CloneConfig(BotConfig src)
{
var dst = new BotConfig();
CopyAllConfig(src, dst);
return dst;
}
///
/// Sao chép CHỈ các thiết lập dùng chung (Hồi máu, Hậu cần, Tiện ích).
/// Giữ nguyên 100% Môn phái, Chiêu tấn công và 3 Slot Chiêu Buff riêng biệt của nhân vật.
///
private static void ApplyCommonSettings(BotConfig src, BotConfig dst)
{
// Phục hồi (Máu / Mana)
dst.HpPump1Enabled = src.HpPump1Enabled;
dst.HpPump1Key = src.HpPump1Key;
dst.HpPump1DelayMs = src.HpPump1DelayMs;
dst.HpPump2Enabled = src.HpPump2Enabled;
dst.HpPump2Key = src.HpPump2Key;
dst.HpPump2DelayMs = src.HpPump2DelayMs;
dst.MpPump1Enabled = src.MpPump1Enabled;
dst.MpPump1Key = src.MpPump1Key;
dst.MpPump1DelayMs = src.MpPump1DelayMs;
dst.MpPump2Enabled = src.MpPump2Enabled;
dst.MpPump2Key = src.MpPump2Key;
dst.MpPump2DelayMs = src.MpPump2DelayMs;
dst.AlarmOnTdp = src.AlarmOnTdp;
// Phân hệ Túi Máu Tân Thủ (Chuẩn Kim Yến)
dst.AutoNewbiePouchEnabled = src.AutoNewbiePouchEnabled;
dst.NewbiePouchIntervalSeconds = src.NewbiePouchIntervalSeconds;
dst.NewbiePouchItemX = src.NewbiePouchItemX;
dst.NewbiePouchItemY = src.NewbiePouchItemY;
dst.NewbiePouchMenuSequence = src.NewbiePouchMenuSequence;
dst.NewbiePouchPotionName = src.NewbiePouchPotionName;
dst.NewbiePouchEmergencyHpPercent = src.NewbiePouchEmergencyHpPercent;
// Tiện ích chung & Điều khiển
dst.FollowLeaderEnabled = src.FollowLeaderEnabled;
dst.LeaderName = src.LeaderName;
dst.FollowDistance = src.FollowDistance;
dst.AutoFollowCrossMap = src.AutoFollowCrossMap;
dst.AutoHorseEnabled = src.AutoHorseEnabled;
dst.AutoLootEnabled = src.AutoLootEnabled;
dst.AutoPartyEnabled = src.AutoPartyEnabled;
dst.AutoTtlEnabled = src.AutoTtlEnabled;
dst.TtlIntervalMinutes = src.TtlIntervalMinutes;
dst.TtlItemX = src.TtlItemX;
dst.TtlItemY = src.TtlItemY;
dst.ScheduledShutdownEnabled = src.ScheduledShutdownEnabled;
dst.ShutdownMinutes = src.ShutdownMinutes;
dst.ScheduledExitGameEnabled = src.ScheduledExitGameEnabled;
dst.ExitGameMinutes = src.ExitGameMinutes;
// Tuần tra tọa độ
dst.PatrolEnabled = src.PatrolEnabled;
dst.WaypointsData = src.WaypointsData;
dst.PatrolIntervalSeconds = src.PatrolIntervalSeconds;
// Hậu cần & Thành thị
dst.PeriodicReturnEnabled = src.PeriodicReturnEnabled;
dst.ReturnIntervalMinutes = src.ReturnIntervalMinutes;
dst.KeyPhuVirtualCode = src.KeyPhuVirtualCode;
dst.MapLoadDelayMs = src.MapLoadDelayMs;
dst.RepairMode = src.RepairMode;
dst.BlacksmithNpcX = src.BlacksmithNpcX;
dst.BlacksmithNpcY = src.BlacksmithNpcY;
dst.RepairItemX = src.RepairItemX;
dst.RepairItemY = src.RepairItemY;
dst.AutoBeginnerMedicineEnabled = src.AutoBeginnerMedicineEnabled;
dst.MedicineIntervalMinutes = src.MedicineIntervalMinutes;
dst.MedicineItemX = src.MedicineItemX;
dst.MedicineItemY = src.MedicineItemY;
// Bắt tùy chọn đối thoại theo text
dst.NpcDialogMatchByText = src.NpcDialogMatchByText;
dst.RepairMenuKeywords = src.RepairMenuKeywords;
dst.RepairSubMenuKeywords = src.RepairSubMenuKeywords;
dst.MedicineMenuKeywords = src.MedicineMenuKeywords;
dst.TravelMenuKeywords = src.TravelMenuKeywords;
dst.NpcDialogFallbackOption = src.NpcDialogFallbackOption;
}
private static void CopyAllConfig(BotConfig src, BotConfig dst)
{
ApplyCommonSettings(src, dst);
dst.DetectedSect = src.DetectedSect;
dst.AutoAttackEnabled = src.AutoAttackEnabled;
dst.AttackMode = src.AttackMode;
dst.AttackIntervalMs = src.AttackIntervalMs;
dst.AttackRadius = src.AttackRadius;
dst.MainAttackSkillName = src.MainAttackSkillName;
dst.Buff1Enabled = src.Buff1Enabled;
dst.Buff1Key = src.Buff1Key;
dst.Buff1IntervalSeconds = src.Buff1IntervalSeconds;
dst.Buff1SkillName = src.Buff1SkillName;
dst.Buff2Enabled = src.Buff2Enabled;
dst.Buff2Key = src.Buff2Key;
dst.Buff2IntervalSeconds = src.Buff2IntervalSeconds;
dst.Buff2SkillName = src.Buff2SkillName;
dst.Buff3Enabled = src.Buff3Enabled;
dst.Buff3Key = src.Buff3Key;
dst.Buff3IntervalSeconds = src.Buff3IntervalSeconds;
dst.Buff3SkillName = src.Buff3SkillName;
dst.AssignLeftSkillEnabled = src.AssignLeftSkillEnabled;
dst.DefaultLeftSkillName = src.DefaultLeftSkillName;
dst.AssignRightSkillEnabled = src.AssignRightSkillEnabled;
dst.DefaultRightSkillName = src.DefaultRightSkillName;
dst.NgaMyHealEnabled = src.NgaMyHealEnabled;
dst.NgaMyHealHpPercent = src.NgaMyHealHpPercent;
dst.NgaMyHealDelayMs = src.NgaMyHealDelayMs;
dst.NgaMyHealParty = src.NgaMyHealParty;
dst.NgaMySupportComboEnabled = src.NgaMySupportComboEnabled;
dst.NgaMySupportComboMode = src.NgaMySupportComboMode;
dst.NgaMySupportIntervalSec = src.NgaMySupportIntervalSec;
dst.CurseRotationEnabled = src.CurseRotationEnabled;
dst.CurseTriggerMode = src.CurseTriggerMode;
dst.Curse1Enabled = src.Curse1Enabled;
dst.Curse1SkillName = src.Curse1SkillName;
dst.Curse1IntervalMs = src.Curse1IntervalMs;
dst.Curse2Enabled = src.Curse2Enabled;
dst.Curse2SkillName = src.Curse2SkillName;
dst.Curse2IntervalMs = src.Curse2IntervalMs;
dst.AutoBuffEnabled = src.AutoBuffEnabled;
dst.BuffKeys = src.BuffKeys;
dst.BuffIntervalSeconds = src.BuffIntervalSeconds;
dst.AutoPartyEnabled = src.AutoPartyEnabled;
dst.IsPartyLeader = src.IsPartyLeader;
dst.PartyMembers = src.PartyMembers;
dst.AutoTtlEnabled = src.AutoTtlEnabled;
dst.TtlIntervalMinutes = src.TtlIntervalMinutes;
dst.TtlItemX = src.TtlItemX;
dst.TtlItemY = src.TtlItemY;
dst.ScheduledShutdownEnabled = src.ScheduledShutdownEnabled;
dst.ShutdownMinutes = src.ShutdownMinutes;
dst.ScheduledExitGameEnabled = src.ScheduledExitGameEnabled;
dst.ExitGameMinutes = src.ExitGameMinutes;
dst.PatrolEnabled = src.PatrolEnabled;
dst.WaypointsData = src.WaypointsData;
dst.PatrolIntervalSeconds = src.PatrolIntervalSeconds;
dst.HpPump1Enabled = src.HpPump1Enabled;
dst.HpPump1Key = src.HpPump1Key;
dst.HpPump1DelayMs = src.HpPump1DelayMs;
dst.HpPump2Enabled = src.HpPump2Enabled;
dst.HpPump2Key = src.HpPump2Key;
dst.HpPump2DelayMs = src.HpPump2DelayMs;
dst.MpPump1Enabled = src.MpPump1Enabled;
dst.MpPump1Key = src.MpPump1Key;
dst.MpPump1DelayMs = src.MpPump1DelayMs;
dst.MpPump2Enabled = src.MpPump2Enabled;
dst.MpPump2Key = src.MpPump2Key;
dst.MpPump2DelayMs = src.MpPump2DelayMs;
dst.AlarmOnTdp = src.AlarmOnTdp;
dst.PeriodicReturnEnabled = src.PeriodicReturnEnabled;
dst.ReturnIntervalMinutes = src.ReturnIntervalMinutes;
dst.KeyPhuVirtualCode = src.KeyPhuVirtualCode;
dst.MapLoadDelayMs = src.MapLoadDelayMs;
dst.RepairMode = src.RepairMode;
dst.BlacksmithNpcX = src.BlacksmithNpcX;
dst.BlacksmithNpcY = src.BlacksmithNpcY;
dst.RepairItemX = src.RepairItemX;
dst.RepairItemY = src.RepairItemY;
dst.AutoBeginnerMedicineEnabled = src.AutoBeginnerMedicineEnabled;
dst.MedicineIntervalMinutes = src.MedicineIntervalMinutes;
dst.MedicineItemX = src.MedicineItemX;
dst.MedicineItemY = src.MedicineItemY;
}
}