using System;
using System.Threading;
using System.Threading.Tasks;
using JXHelper.Config;
using JXHelper.Platform;
namespace JXHelper.Core;
///
/// Động cơ tác chiến tự hành (Combat Engine - Chuẩn Tab L.công AutoVLBS).
/// Chuyên trách: Tự đánh ngầm (cự ly/bán kính), nhặt đồ (Space), theo sau đội trưởng, và 3 slot buff độc lập.
/// Tuân thủ nghiêm ngặt bất biến: INV-BG-INPUT-NO-HIJACK.
///
public class CombatEngine : IDisposable
{
private CancellationTokenSource? _cts;
private Task? _combatLoopTask;
private DateTime _lastBuff1Time = DateTime.MinValue;
private DateTime _lastBuff2Time = DateTime.MinValue;
private DateTime _lastBuff3Time = DateTime.MinValue;
private DateTime _lastLootTime = DateTime.MinValue;
private DateTime _lastFollowTime = DateTime.MinValue;
private DateTime _lastNgaMyHealTime = DateTime.MinValue;
private DateTime _lastNgaMySupport1Time = DateTime.MinValue;
private DateTime _lastNgaMySupport2Time = DateTime.MinValue;
private DateTime _lastCurse1Time = DateTime.MinValue;
private DateTime _lastCurse2Time = DateTime.MinValue;
private DateTime _lastNguDocCurseChainTime = DateTime.MinValue;
private readonly Random _rand = new();
public bool IsRunning { get; private set; }
public bool IsPaused { get; set; }
public event Action? OnLog;
public void Start(IntPtr hWnd, BotConfig config)
{
if (IsRunning) return;
IsRunning = true;
IsPaused = false;
_cts = new CancellationTokenSource();
_combatLoopTask = Task.Run(() => CombatLoopAsync(hWnd, config, _cts.Token));
OnLog?.Invoke("[L.CÔNG] Động cơ chiến đấu tự động đã khởi động.");
if (!string.IsNullOrEmpty(config.MainAttackSkillName))
{
OnLog?.Invoke($"[L.CÔNG] Chiêu công kích: '{config.MainAttackSkillName}' ({(config.AttackMode == 1 ? "Chuột trái" : "Chuột phải")})");
}
if (config.AssignLeftSkillEnabled && !string.IsNullOrEmpty(config.DefaultLeftSkillName))
{
OnLog?.Invoke($"[KHỞI ĐỘNG] Gán chiêu tay trái mặc định: {config.DefaultLeftSkillName}");
}
if (config.AssignRightSkillEnabled && !string.IsNullOrEmpty(config.DefaultRightSkillName))
{
OnLog?.Invoke($"[KHỞI ĐỘNG] Gán chiêu tay phải mặc định: {config.DefaultRightSkillName}");
}
}
public void Stop()
{
if (!IsRunning) return;
IsRunning = false;
IsPaused = false;
_cts?.Cancel();
try
{
_combatLoopTask?.Wait(500);
}
catch
{
// Bỏ qua lỗi hủy task
}
_cts?.Dispose();
_cts = null;
_combatLoopTask = null;
OnLog?.Invoke("[L.CÔNG] Đã dừng động cơ chiến đấu.");
}
private async Task CombatLoopAsync(IntPtr hWnd, BotConfig config, CancellationToken token)
{
while (!token.IsCancellationRequested)
{
try
{
if (!IsPaused && config.AutoAttackEnabled)
{
var now = DateTime.Now;
// 1. Kiểm tra 3 Slot Chiêu hỗ trợ độc lập (Buff skills)
if (config.Buff1Enabled && (now - _lastBuff1Time).TotalSeconds >= Math.Max(5, config.Buff1IntervalSeconds))
{
await CastBuffAsync(hWnd, config.Buff1Key, 1, config.Buff1SkillName);
_lastBuff1Time = now;
}
if (config.Buff2Enabled && (now - _lastBuff2Time).TotalSeconds >= Math.Max(5, config.Buff2IntervalSeconds))
{
await CastBuffAsync(hWnd, config.Buff2Key, 2, config.Buff2SkillName);
_lastBuff2Time = now;
}
if (config.Buff3Enabled && (now - _lastBuff3Time).TotalSeconds >= Math.Max(5, config.Buff3IntervalSeconds))
{
await CastBuffAsync(hWnd, config.Buff3Key, 3, config.Buff3SkillName);
_lastBuff3Time = now;
}
// 1.1 Phân hệ Nga My: Bơm Từ Hàng Phổ Độ & Hỗ trợ combo (Chuẩn AutoVLBS)
if (config.NgaMyHealEnabled && (now - _lastNgaMyHealTime).TotalMilliseconds >= Math.Max(1000, config.NgaMyHealDelayMs))
{
string targetDesc = config.NgaMyHealParty ? "bản thân & cả nhóm" : "bản thân";
OnLog?.Invoke($"[NGA MY] Bơm Từ Hàng Phổ Độ ({targetDesc}) qua Chuột phải...");
await BackgroundInput.SendRightClickAsync(hWnd, 400, 300);
_lastNgaMyHealTime = now;
await Task.Delay(200, token);
}
if (config.NgaMySupportComboEnabled)
{
int supSec = Math.Max(10, config.NgaMySupportIntervalSec);
string skill1 = config.NgaMySupportComboMode switch
{
1 => "Phật Tâm Từ Hữu",
2 => "Phật Tâm Từ Hữu",
_ => "Lưu Thủy"
};
string skill2 = config.NgaMySupportComboMode switch
{
1 => "Lưu Thủy",
2 => "Mộng Điệp",
_ => "Mộng Điệp"
};
if ((now - _lastNgaMySupport1Time).TotalSeconds >= supSec)
{
OnLog?.Invoke($"[NGA MY] Thi triển hỗ trợ #1 [{skill1}]...");
await BackgroundInput.SendRightClickAsync(hWnd, 400, 300);
_lastNgaMySupport1Time = now;
await Task.Delay(200, token);
}
else if ((now - _lastNgaMySupport2Time).TotalSeconds >= supSec)
{
OnLog?.Invoke($"[NGA MY] Thi triển hỗ trợ #2 [{skill2}]...");
await BackgroundInput.SendRightClickAsync(hWnd, 400, 300);
_lastNgaMySupport2Time = now;
await Task.Delay(200, token);
}
}
// 2. Tự động tìm & bám sát Acc Chính (Chuẩn Auto Kim Yến)
if (config.FollowLeaderEnabled && (now - _lastFollowTime).TotalSeconds >= 1.5)
{
await ExecuteFollowLeaderAsync(hWnd, config, token);
_lastFollowTime = now;
}
// 3. Tính toán tọa độ xuất chiêu theo cự ly / bán kính (Khóa bãi Nghiệt Long Động 7x)
int targetX = 400;
int targetY = 300;
if (!config.LockCurrentSpot && config.AttackRadius > 0)
{
int r = config.AttackRadius;
int dx = _rand.Next(-r, r);
int dy = _rand.Next(-r, r);
targetX = Math.Clamp(400 + dx, 60, 740);
targetY = Math.Clamp(300 + dy, 60, 540);
}
else if (config.LockCurrentSpot)
{
// Đứng yên tại bãi: Chỉ điều chỉnh góc bắn nhẹ quanh tâm để tung chưởng đa hướng
int dx = _rand.Next(-30, 30);
int dy = _rand.Next(-30, 30);
targetX = 400 + dx;
targetY = 300 + dy;
}
// 3.1 Phân hệ Ngũ Độc: Chuỗi bùa Kim Yến (Pre-Attack Curse Pipeline) & Luân chuyển
if (config.NguDocCurseChainEnabled)
{
int durationSec = Math.Max(5, config.NguDocCurseDurationSec);
if ((now - _lastNguDocCurseChainTime).TotalSeconds >= durationSec)
{
await ExecuteNguDocCurseChainAsync(hWnd, config, targetX, targetY, token);
_lastNguDocCurseChainTime = DateTime.Now;
}
}
else if (config.CurseRotationEnabled)
{
if (config.Curse1Enabled && (now - _lastCurse1Time).TotalMilliseconds >= Math.Max(1000, config.Curse1IntervalMs))
{
string c1 = !string.IsNullOrEmpty(config.Curse1SkillName) ? config.Curse1SkillName : "Bùa luân chuyển #1";
OnLog?.Invoke($"[NGŨ ĐỘC] Quăng bùa luân chuyển [{c1}] vào mục tiêu ({targetX}, {targetY})...");
await BackgroundInput.SendRightClickAsync(hWnd, targetX, targetY);
_lastCurse1Time = now;
await Task.Delay(250, token);
}
if (config.Curse2Enabled && (now - _lastCurse2Time).TotalMilliseconds >= Math.Max(1000, config.Curse2IntervalMs))
{
string c2 = !string.IsNullOrEmpty(config.Curse2SkillName) ? config.Curse2SkillName : "Bùa luân chuyển #2";
OnLog?.Invoke($"[NGŨ ĐỘC] Quăng bùa luân chuyển [{c2}] vào mục tiêu ({targetX}, {targetY})...");
await BackgroundInput.SendRightClickAsync(hWnd, targetX, targetY);
_lastCurse2Time = now;
await Task.Delay(250, token);
}
}
// 4. Xuất chiêu tự đánh ngầm (Kháng Long Hữu Hối / Chiêu công kích)
string attackSkillName = !string.IsNullOrEmpty(config.MainAttackSkillName) ? config.MainAttackSkillName : "Chiêu công kích";
if (config.AttackMode == 1)
{
await BackgroundInput.SendLeftClickAsync(hWnd, targetX, targetY);
}
else
{
await BackgroundInput.SendRightClickAsync(hWnd, targetX, targetY);
}
// 5. Tự động nhặt đồ xung quanh (Spacebar) mỗi 600ms
if (config.AutoLootEnabled && (now - _lastLootTime).TotalMilliseconds >= 600)
{
await BackgroundInput.SendKeyAsync(hWnd, 0x20); // VK_SPACE
_lastLootTime = now;
}
}
int interval = Math.Max(100, config.AttackIntervalMs);
await Task.Delay(interval, token);
}
catch (OperationCanceledException)
{
break;
}
catch (Exception ex)
{
OnLog?.Invoke($"[L.CÔNG LỖI] {ex.Message}");
await Task.Delay(1000, token);
}
}
}
private async Task CastBuffAsync(IntPtr hWnd, int vkKey, int slotIndex, string skillName = "")
{
string nameDesc = !string.IsNullOrWhiteSpace(skillName) ? $"[{skillName}]" : $"Slot {slotIndex}";
if (vkKey == 0x02 || vkKey <= 0) // Chuột phải (VK_RBUTTON) mặc định
{
OnLog?.Invoke($"[BUFF] Thi triển chiêu hỗ trợ {nameDesc} qua Chuột phải...");
await BackgroundInput.SendRightClickAsync(hWnd, 400, 300);
}
else
{
OnLog?.Invoke($"[BUFF] Kích hoạt chiêu hỗ trợ {nameDesc} (phím võ công 0x{vkKey:X2})...");
await BackgroundInput.SendKeyAsync(hWnd, vkKey);
await Task.Delay(200);
await BackgroundInput.SendRightClickAsync(hWnd, 400, 300);
}
await Task.Delay(250);
}
private async Task ExecuteNguDocCurseChainAsync(IntPtr hWnd, BotConfig config, int targetX, int targetY, CancellationToken token)
{
int delay = Math.Clamp(config.NguDocCurseDelayMs, 100, 1500);
string bossTag = config.NguDocBossOnly ? "[BOSS/TINH ANH] " : "";
// 1. U Hồn Phệ Phách (Giảm tốc & Giảm kháng)
if (config.NguDocCurseUHon)
{
OnLog?.Invoke($"[NGŨ ĐỘC-KIM YẾN] {bossTag}Thi triển Bùa 1: [U Hồn Phệ Phách] (Chậm & Giảm kháng) -> ({targetX}, {targetY})");
await BackgroundInput.SendRightClickAsync(hWnd, targetX, targetY);
await Task.Delay(delay, token);
}
// 2. Vạn Cổ Thực Tâm (Rút máu & Giảm kháng độc)
if (config.NguDocCurseVanCo)
{
OnLog?.Invoke($"[NGŨ ĐỘC-KIM YẾN] {bossTag}Thi triển Bùa 2: [Vạn Cổ Thực Tâm] (Rút máu & Hạ kháng độc) -> ({targetX}, {targetY})");
await BackgroundInput.SendRightClickAsync(hWnd, targetX, targetY);
await Task.Delay(delay, token);
}
// 3. Thi Độc Hủ Nhục (Ăn mòn giáp & Suy yếu)
if (config.NguDocCurseThiDoc)
{
OnLog?.Invoke($"[NGŨ ĐỘC-KIM YẾN] {bossTag}Thi triển Bùa 3: [Thi Độc Hủ Nhục] (Ăn mòn giáp & Suy yếu) -> ({targetX}, {targetY})");
await BackgroundInput.SendRightClickAsync(hWnd, targetX, targetY);
await Task.Delay(delay, token);
}
// 4. Hóa Cốt Miên Chưởng (Hút sinh lực)
if (config.NguDocCurseHoaCot)
{
OnLog?.Invoke($"[NGŨ ĐỘC-KIM YẾN] {bossTag}Thi triển Bùa 4: [Hóa Cốt Miên Chưởng] (Hút sinh lực) -> ({targetX}, {targetY})");
await BackgroundInput.SendRightClickAsync(hWnd, targetX, targetY);
await Task.Delay(delay, token);
}
string attackSkill = !string.IsNullOrEmpty(config.MainAttackSkillName) ? config.MainAttackSkillName : "Bách Độc Xuyên Tâm";
OnLog?.Invoke($"[NGŨ ĐỘC-KIM YẾN] Hoàn tất chuỗi bùa! Tráo lại chiêu công kích chính: [{attackSkill}]");
await Task.Delay(100, token);
}
///
/// Tự động tìm và bám sát Acc Chính (Leader) - Học hỏi từ cơ chế TheoSau của Auto Kim Yến.
/// Hỗ trợ: Tự bay THP khi khác Map, và tính vector khoảng cách thời gian thực khi cùng Map.
/// Tuân thủ nghiêm ngặt bất biến: INV-BG-INPUT-NO-HIJACK & INV-SMART-LEADER-FOLLOW.
///
private async Task ExecuteFollowLeaderAsync(IntPtr hWnd, BotConfig config, CancellationToken token)
{
if (string.IsNullOrWhiteSpace(config.LeaderName)) return;
try
{
// 1. Đọc thông số thời gian thực của bản thân (Follower)
NativeMethods.GetWindowThreadProcessId(hWnd, out uint myPid);
var selfStats = PlayerStatsReader.ReadLiveStats((int)myPid);
// 2. Tìm và đọc thông số thời gian thực của Acc Chính (Leader)
var leaderStats = PlayerStatsReader.FindCharacterLiveStats(config.LeaderName);
if (leaderStats == null)
{
// Fallback nếu chưa đọc được RAM leader: click nhẹ quanh tâm
await BackgroundInput.SendLeftClickAsync(hWnd, 400, 300);
return;
}
// 3. Trường hợp KHÁC MAP: Acc chính ở bãi train, acc phụ ở thành (chết hồi sinh hoặc phù về)
if (selfStats != null && leaderStats.MapId > 0 && selfStats.MapId > 0 && selfStats.MapId != leaderStats.MapId)
{
if (config.AutoFollowCrossMap)
{
OnLog?.Invoke($"[TÌM ACC CHÍNH] Acc chính '{config.LeaderName}' đang ở {leaderStats.MapName} (Map {leaderStats.MapId}), acc phụ đang ở {selfStats.MapName} (Map {selfStats.MapId}). Tiến hành dịch chuyển...");
var thp = new ThanHanhPhuExecutor(hWnd, config);
bool teleportSuccess = await thp.ExecuteTeleportToMapAsync(leaderStats.MapId);
if (teleportSuccess)
{
OnLog?.Invoke($"[TÌM ACC CHÍNH] Đã dịch chuyển đến map {leaderStats.MapName} của Acc Chính thành công!");
}
await Task.Delay(2500, token);
return;
}
}
// 4. Trường hợp CÙNG MAP: Tính toán khoảng cách & vector bám sát
if (selfStats != null && (selfStats.CoordX > 0 || selfStats.HudX > 0) && (leaderStats.CoordX > 0 || leaderStats.HudX > 0))
{
double dx, dy;
double dist;
if (selfStats.CoordX > 0 && leaderStats.CoordX > 0)
{
dx = leaderStats.CoordX - selfStats.CoordX;
dy = leaderStats.CoordY - selfStats.CoordY;
dist = Math.Sqrt(dx * dx + dy * dy) / 256.0 * 32.0; // quy đổi pixel màn hình xấp xỉ
}
else
{
dx = (leaderStats.HudX - selfStats.HudX) * 256.0;
dy = (leaderStats.HudY - selfStats.HudY) * 512.0;
dist = Math.Sqrt((leaderStats.HudX - selfStats.HudX) * (leaderStats.HudX - selfStats.HudX) +
(leaderStats.HudY - selfStats.HudY) * (leaderStats.HudY - selfStats.HudY)) * 32.0;
}
int safeFollowDistance = Math.Max(60, config.FollowDistance);
if (dist > safeFollowDistance)
{
// Chuyển đổi sang hệ tọa độ màn hình Isometric của JX1 (800x600, center 400, 300)
double isoX = (dx - dy) / 2.0;
double isoY = (dx + dy) / 4.0;
double isoLen = Math.Sqrt(isoX * isoX + isoY * isoY);
if (isoLen > 0.001)
{
// Giới hạn bán kính click chuột tối đa 200px (X) và 140px (Y) từ tâm màn hình
int stepX = (int)Math.Round(isoX / isoLen * Math.Min(200.0, isoLen));
int stepY = (int)Math.Round(isoY / isoLen * Math.Min(140.0, isoLen));
int clickX = Math.Clamp(400 + stepX, 100, 700);
int clickY = Math.Clamp(300 + stepY, 80, 500);
OnLog?.Invoke($"[BÁM ACC CHÍNH] Bám theo '{config.LeaderName}' (cự ly ~{dist:F0}px -> Click ({clickX}, {clickY}))...");
await BackgroundInput.SendLeftClickAsync(hWnd, clickX, clickY);
}
}
}
else
{
// Fallback khi không có tọa độ RAM
await BackgroundInput.SendLeftClickAsync(hWnd, 400, 300);
}
}
catch (OperationCanceledException)
{
// Bỏ qua
}
catch (Exception ex)
{
OnLog?.Invoke($"[LỖI BÁM ACC CHÍNH] {ex.Message}");
}
}
public void Dispose()
{
Stop();
}
}