using System;
using System.Threading;
using System.Threading.Tasks;
using JXHelper.Common;
using JXHelper.Config;
using JXHelper.Platform;
namespace JXHelper.Core;
///
/// Thực thi tuần tự chuỗi hành vi Về thành, Sửa đồ (chọn dialog 1) & Quay lại bãi bằng Thần Hành Phù.
/// Tuân thủ nghiêm ngặt bất biến: INV-SAFE-REPAIR-SEQUENCE & INV-BG-INPUT-NO-HIJACK.
///
public static class ActionExecutor
{
public static async Task ExecuteTownReturnAndRepairAsync(
IntPtr hWnd,
BotConfig config,
Action log,
CancellationToken cancellationToken)
{
if (hWnd == IntPtr.Zero || !NativeMethods.IsWindow(hWnd))
{
log("[LỖI] Cửa sổ game không tồn tại hoặc đã bị đóng!");
return false;
}
try
{
// 1. Kích hoạt Thổ Địa Phù
log($"[VỀ THÀNH] Gửi phím Phù ngầm (VK: 0x{config.KeyPhuVirtualCode:X2})...");
await BackgroundInput.SendKeyAsync(hWnd, config.KeyPhuVirtualCode);
// 2. Chờ nạp map về thành
log($"[CHỜ MAP] Đang chờ {config.MapLoadDelayMs}ms để nạp bản đồ thành thị ({config.TargetCityName})...");
await Task.Delay(config.MapLoadDelayMs, cancellationToken);
// 3. Tự động lưu rương / điểm hồi sinh tại thành thị
if (config.AutoSaveRespawnEnabled)
{
await ExecuteSaveRespawnAsync(hWnd, config, log, cancellationToken);
await Task.Delay(400, cancellationToken);
}
// 4. Mở rương hành trang
log($"[HÀNH TRANG] Mở hành trang (VK: 0x{config.KeyBagVirtualCode:X2})...");
await BackgroundInput.SendKeyAsync(hWnd, config.KeyBagVirtualCode);
await Task.Delay(Constants.DefaultBagOpenDelayMs, cancellationToken);
// 5. Thực hiện Sửa đồ theo chế độ đã cấu hình
if (config.AutoRepairEnabled)
{
int fallback = config.NpcDialogFallbackOption > 0 ? config.NpcDialogFallbackOption : 1;
if (config.RepairMode == 1) // Sửa tại Thợ Rèn trong thành (Cấp >= 60)
{
log($"[THỢ RÈN] Click NPC Thợ Rèn tại ({config.BlacksmithNpcX}, {config.BlacksmithNpcY})...");
await BackgroundInput.SendLeftClickAsync(hWnd, config.BlacksmithNpcX, config.BlacksmithNpcY);
await Task.Delay(600, cancellationToken);
if (config.RepairDialogSelectOption1)
{
// Cấp 1: Bắt tùy chọn Sửa trang bị theo nội dung text
int vk1 = NpcDialogTextMatcher.GetVirtualKeyForIndex(fallback);
char ch1 = NpcDialogTextMatcher.GetCharForIndex(fallback);
log($"[THỢ RÈN] Chọn menu 'Sửa trang bị' (Từ khóa: '{config.RepairMenuKeywords}' -> Phím {ch1})...");
await BackgroundInput.SendKeyAsync(hWnd, vk1);
await BackgroundInput.SendCharAsync(hWnd, ch1);
await Task.Delay(Constants.DefaultRepairEffectDelayMs, cancellationToken);
// Cấp 2: Bắt tùy chọn Sửa toàn bộ theo nội dung text
int vk2 = NpcDialogTextMatcher.GetVirtualKeyForIndex(fallback);
char ch2 = NpcDialogTextMatcher.GetCharForIndex(fallback);
log($"[THỢ RÈN] Chọn dòng 'Sửa toàn bộ' (Từ khóa: '{config.RepairSubMenuKeywords}' -> Phím {ch2})...");
await BackgroundInput.SendKeyAsync(hWnd, vk2);
await BackgroundInput.SendCharAsync(hWnd, ch2);
await Task.Delay(300, cancellationToken);
await BackgroundInput.SendKeyAsync(hWnd, 0x20); // Space
await Task.Delay(200, cancellationToken);
}
}
else // Sửa bằng Túi Đồ Nghề (Cấp < 60)
{
log($"[HÀNH TRANG] Mở hành trang (VK: 0x{config.KeyBagVirtualCode:X2})...");
await BackgroundInput.SendKeyAsync(hWnd, config.KeyBagVirtualCode);
await Task.Delay(Constants.DefaultBagOpenDelayMs, cancellationToken);
log($"[SỬA ĐỒ] Click chuột phải Túi đồ nghề tại ({config.RepairItemX}, {config.RepairItemY})...");
await BackgroundInput.SendRightClickAsync(hWnd, config.RepairItemX, config.RepairItemY);
if (config.RepairDialogSelectOption1)
{
await Task.Delay(config.RepairDialogDelayMs, cancellationToken);
int vk = NpcDialogTextMatcher.GetVirtualKeyForIndex(fallback);
char ch = NpcDialogTextMatcher.GetCharForIndex(fallback);
log($"[SỬA ĐỒ] Bảng thoại xuất hiện -> Chọn dòng theo text ('{config.RepairSubMenuKeywords}' -> Phím {ch})...");
await BackgroundInput.SendKeyAsync(hWnd, vk);
await BackgroundInput.SendCharAsync(hWnd, ch);
await Task.Delay(Constants.DefaultRepairEffectDelayMs, cancellationToken);
await BackgroundInput.SendKeyAsync(hWnd, 0x20); // Space
await Task.Delay(200, cancellationToken);
}
// Đóng túi sau khi sửa
await BackgroundInput.SendKeyAsync(hWnd, config.KeyBagVirtualCode);
await Task.Delay(200, cancellationToken);
}
}
// 5. Tự động dùng Dược Phẩm Tân Thủ (chu kỳ 3h / 180 phút)
if (config.AutoBeginnerMedicineEnabled)
{
long nowTicks = DateTime.UtcNow.Ticks;
long elapsedTicks = nowTicks - config.LastMedicineUseTimestamp;
long intervalTicks = TimeSpan.FromMinutes(config.MedicineIntervalMinutes).Ticks;
if (config.LastMedicineUseTimestamp == 0 || elapsedTicks >= intervalTicks)
{
log($"[DƯỢC PHẨM TÂN THỦ] Đủ chu kỳ {config.MedicineIntervalMinutes} phút -> Mở F4 dùng thuốc tại ({config.MedicineItemX}, {config.MedicineItemY})...");
await BackgroundInput.SendKeyAsync(hWnd, config.KeyBagVirtualCode);
await Task.Delay(Constants.DefaultBagOpenDelayMs, cancellationToken);
await BackgroundInput.SendRightClickAsync(hWnd, config.MedicineItemX, config.MedicineItemY);
await Task.Delay(400, cancellationToken);
// Chọn dòng nhận dược phẩm nếu có bảng thoại
await BackgroundInput.SendKeyAsync(hWnd, 0x31);
await BackgroundInput.SendCharAsync(hWnd, '1');
await Task.Delay(300, cancellationToken);
await BackgroundInput.SendKeyAsync(hWnd, 0x20); // Space
config.LastMedicineUseTimestamp = nowTicks;
config.Save();
log("[DƯỢC PHẨM TÂN THỦ] Đã kích hoạt nhận Dược Phẩm Tân Thủ thành công!");
await BackgroundInput.SendKeyAsync(hWnd, config.KeyBagVirtualCode);
await Task.Delay(200, cancellationToken);
}
else
{
var remaining = TimeSpan.FromTicks(intervalTicks - elapsedTicks);
log($"[DƯỢC PHẨM TÂN THỦ] Chưa đến giờ dùng lại (còn {remaining.Hours:D2}h:{remaining.Minutes:D2}p).");
}
}
// 6. Tự động quay lại bãi luyện công bằng Thần Hành Phù / Phù cấp cao
if (config.AutoTravelBackEnabled)
{
log($"[QUAY LẠI BÃI] Mở F4 click Thần Hành Phù tại ({config.TravelItemX}, {config.TravelItemY})...");
await BackgroundInput.SendKeyAsync(hWnd, config.KeyBagVirtualCode);
await Task.Delay(Constants.DefaultBagOpenDelayMs, cancellationToken);
await BackgroundInput.SendRightClickAsync(hWnd, config.TravelItemX, config.TravelItemY);
await Task.Delay(800, cancellationToken);
// Duyệt qua các phím menu cấu hình (ví dụ: "2,7,1" cho Nghiệt Long Động)
var menuKeys = config.TravelMenuKeys.Split(',', StringSplitOptions.RemoveEmptyEntries);
foreach (var keyStr in menuKeys)
{
string trimmed = keyStr.Trim();
if (trimmed.Length > 0 && char.IsDigit(trimmed[0]))
{
int delay = Math.Max(config.TravelMenuDelayMs, 600);
await Task.Delay(delay, cancellationToken);
int vk = 0x30 + (trimmed[0] - '0');
log($"[QUAY LẠI BÃI] Gửi phím '{trimmed[0]}' chọn mục menu dịch chuyển...");
await BackgroundInput.SendKeyAsync(hWnd, vk);
await BackgroundInput.SendCharAsync(hWnd, trimmed[0]);
}
}
await Task.Delay(500, cancellationToken);
await BackgroundInput.SendKeyAsync(hWnd, config.KeyBagVirtualCode); // Đóng rương F4
// Chờ teleport đổi map sang bãi luyện
log($"[QUAY LẠI BÃI] Đang chờ nạp bản đồ bãi luyện công ({config.MapLoadDelayMs}ms)...");
await Task.Delay(config.MapLoadDelayMs, cancellationToken);
}
log("[THÀNH CÔNG] Chu kỳ tự hành (Về thành -> Sửa đồ -> Dược phẩm -> Bãi) đã hoàn tất 100% ngầm!");
return true;
}
catch (OperationCanceledException)
{
log("[DỪNG] Chu kỳ đã bị người dùng hủy bỏ.");
return false;
}
catch (Exception ex)
{
log($"[LỖI THỰC THI] {ex.Message}");
return false;
}
}
///
/// Kiểm thử sửa đồ độc lập
///
public static async Task ExecuteSingleRepairAsync(IntPtr hWnd, BotConfig config, Action log)
{
int fallback = config.NpcDialogFallbackOption > 0 ? config.NpcDialogFallbackOption : 1;
int vk = NpcDialogTextMatcher.GetVirtualKeyForIndex(fallback);
char ch = NpcDialogTextMatcher.GetCharForIndex(fallback);
if (config.RepairMode == 1)
{
log($"[TEST THỢ RÈN] Thử click NPC Thợ Rèn tại ({config.BlacksmithNpcX}, {config.BlacksmithNpcY})...");
await BackgroundInput.SendLeftClickAsync(hWnd, config.BlacksmithNpcX, config.BlacksmithNpcY);
await Task.Delay(600);
if (config.RepairDialogSelectOption1)
{
log($"[TEST THỢ RÈN] Gửi phím {ch} chọn 'Sửa trang bị' (Text: '{config.RepairMenuKeywords}')...");
await BackgroundInput.SendKeyAsync(hWnd, vk);
await BackgroundInput.SendCharAsync(hWnd, ch);
await Task.Delay(Constants.DefaultRepairEffectDelayMs);
log($"[TEST THỢ RÈN] Gửi phím {ch} chọn 'Sửa toàn bộ' (Text: '{config.RepairSubMenuKeywords}')...");
await BackgroundInput.SendKeyAsync(hWnd, vk);
await BackgroundInput.SendCharAsync(hWnd, ch);
await Task.Delay(300);
await BackgroundInput.SendKeyAsync(hWnd, 0x20);
}
log("[TEST THỢ RÈN] Hoàn tất thử nghiệm sửa đồ Thợ Rèn.");
}
else
{
log($"[TEST TÚI ĐỒ NGHỀ] Mở F4 -> Click Túi đồ nghề -> Chọn dòng theo text (Phím {ch})...");
await BackgroundInput.SendKeyAsync(hWnd, config.KeyBagVirtualCode);
await Task.Delay(Constants.DefaultBagOpenDelayMs);
await BackgroundInput.SendRightClickAsync(hWnd, config.RepairItemX, config.RepairItemY);
if (config.RepairDialogSelectOption1)
{
await Task.Delay(config.RepairDialogDelayMs);
await BackgroundInput.SendKeyAsync(hWnd, vk);
await BackgroundInput.SendCharAsync(hWnd, ch);
await Task.Delay(Constants.DefaultRepairEffectDelayMs);
await BackgroundInput.SendKeyAsync(hWnd, 0x20);
}
await Task.Delay(200);
await BackgroundInput.SendKeyAsync(hWnd, config.KeyBagVirtualCode);
log("[TEST TÚI ĐỒ NGHỀ] Hoàn tất thử nghiệm Túi Đồ Nghề.");
}
}
///
/// Kiểm thử dùng Dược Phẩm Tân Thủ độc lập
///
public static async Task ExecuteSingleMedicineAsync(IntPtr hWnd, BotConfig config, Action log)
{
log($"[TEST DƯỢC PHẨM] Mở F4 click item Dược Phẩm Tân Thủ tại ({config.MedicineItemX}, {config.MedicineItemY})...");
await BackgroundInput.SendKeyAsync(hWnd, config.KeyBagVirtualCode);
await Task.Delay(Constants.DefaultBagOpenDelayMs);
await BackgroundInput.SendRightClickAsync(hWnd, config.MedicineItemX, config.MedicineItemY);
await Task.Delay(400);
await BackgroundInput.SendKeyAsync(hWnd, 0x31);
await BackgroundInput.SendCharAsync(hWnd, '1');
await Task.Delay(300);
await BackgroundInput.SendKeyAsync(hWnd, 0x20);
await Task.Delay(200);
await BackgroundInput.SendKeyAsync(hWnd, config.KeyBagVirtualCode);
log("[TEST DƯỢC PHẨM] Hoàn tất thử nghiệm Dược Phẩm Tân Thủ.");
}
///
/// Kiểm thử tự dùng Thần Hành Phù quay lại bãi luyện công độc lập
///
public static async Task ExecuteSingleTravelBackAsync(IntPtr hWnd, BotConfig config, Action log)
{
log($"[TEST THẦN HÀNH PHÙ] Mở F4 click Thần Hành Phù tại ({config.TravelItemX}, {config.TravelItemY})...");
await BackgroundInput.SendKeyAsync(hWnd, config.KeyBagVirtualCode);
await Task.Delay(Constants.DefaultBagOpenDelayMs);
await BackgroundInput.SendRightClickAsync(hWnd, config.TravelItemX, config.TravelItemY);
await Task.Delay(800);
var menuKeys = config.TravelMenuKeys.Split(',', StringSplitOptions.RemoveEmptyEntries);
foreach (var keyStr in menuKeys)
{
string trimmed = keyStr.Trim();
if (trimmed.Length > 0 && char.IsDigit(trimmed[0]))
{
int delay = Math.Max(config.TravelMenuDelayMs, 600);
await Task.Delay(delay);
int vk = 0x30 + (trimmed[0] - '0');
log($"[TEST THẦN HÀNH PHÙ] Gửi phím '{trimmed[0]}' chọn menu dịch chuyển...");
await BackgroundInput.SendKeyAsync(hWnd, vk);
await BackgroundInput.SendCharAsync(hWnd, trimmed[0]);
}
}
await Task.Delay(500);
await BackgroundInput.SendKeyAsync(hWnd, config.KeyBagVirtualCode); // Đóng F4
log($"[TEST THẦN HÀNH PHÙ] Đang dịch chuyển... Chờ nạp bản đồ ({config.MapLoadDelayMs}ms)...");
await Task.Delay(config.MapLoadDelayMs);
log("[TEST THẦN HÀNH PHÙ] Hoàn tất truyền tống bằng Thần Hành Phù.");
}
///
/// Thực hiện tự động Lưu điểm hồi sinh tại thành thị hiện tại (Dương Châu) bằng Thần Hành Phù.
///
public static async Task ExecuteSaveRespawnAsync(
IntPtr hWnd,
BotConfig config,
Action log,
CancellationToken cancellationToken = default)
{
try
{
log($"[LƯU RƯƠNG] Mở F4 click Thần Hành Phù tại ({config.TravelItemX}, {config.TravelItemY}) để lưu điểm hồi sinh ({config.TargetCityName})...");
await BackgroundInput.SendKeyAsync(hWnd, config.KeyBagVirtualCode);
await Task.Delay(Constants.DefaultBagOpenDelayMs, cancellationToken);
await BackgroundInput.SendRightClickAsync(hWnd, config.TravelItemX, config.TravelItemY);
await Task.Delay(800, cancellationToken);
int vk = 0x30 + (config.RespawnMenuKey % 10);
char ch = (char)('0' + (config.RespawnMenuKey % 10));
log($"[LƯU RƯƠNG] Gửi phím '{ch}' chọn 'Lưu điểm hồi sinh'...");
await BackgroundInput.SendKeyAsync(hWnd, vk);
await BackgroundInput.SendCharAsync(hWnd, ch);
await Task.Delay(400, cancellationToken);
await BackgroundInput.SendKeyAsync(hWnd, config.KeyBagVirtualCode); // Đóng F4
log($"[LƯU RƯƠNG] Đã lưu thành công điểm hồi sinh tại {config.TargetCityName}!");
return true;
}
catch (Exception ex)
{
log($"[LƯU RƯƠNG] Lỗi khi lưu điểm hồi sinh: {ex.Message}");
return false;
}
}
///
/// Tự động sử dụng Túi Máu Tân Thủ để nạp đầy bình máu (Ngũ Hoa Ngọc Lộ Hoàn) - Học hỏi từ Auto Kim Yến.
/// Chuỗi an toàn: Mở F4 -> Chuột phải ô Túi Máu -> Chọn menu (ví dụ 1, 1) -> Đóng F4.
/// Tuân thủ nghiêm ngặt bất biến: INV-BG-INPUT-NO-HIJACK & INV-NEWBIE-BLOOD-POUCH.
///
public static async Task ExecuteUseNewbiePouchAsync(
IntPtr hWnd,
BotConfig config,
Action? log,
CancellationToken cancellationToken)
{
if (hWnd == IntPtr.Zero || !NativeMethods.IsWindow(hWnd)) return false;
try
{
log?.Invoke($"[TÚI MÁU TÂN THỦ] Mở rương F4 sử dụng Túi Máu tại ({config.NewbiePouchItemX}, {config.NewbiePouchItemY})...");
// 1. Mở rương F4
await BackgroundInput.SendKeyAsync(hWnd, config.KeyBagVirtualCode);
await Task.Delay(Constants.DefaultBagOpenDelayMs, cancellationToken);
// 2. Click chuột phải ngầm vào ô Túi Máu Tân Thủ
await BackgroundInput.SendRightClickAsync(hWnd, config.NewbiePouchItemX, config.NewbiePouchItemY);
await Task.Delay(500, cancellationToken);
// 3. Xử lý chuỗi menu lựa chọn (ví dụ: 1, 1 để lấy Ngũ Hoa Ngọc Lộ Hoàn)
if (!string.IsNullOrWhiteSpace(config.NewbiePouchMenuSequence))
{
var keys = config.NewbiePouchMenuSequence.Split(new[] { ',', ';', '_' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var k in keys)
{
if (int.TryParse(k.Trim(), out int optIndex))
{
int vk = NpcDialogTextMatcher.GetVirtualKeyForIndex(optIndex);
char ch = NpcDialogTextMatcher.GetCharForIndex(optIndex);
log?.Invoke($"[TÚI MÁU TÂN THỦ] Chọn menu dòng {optIndex} (Phím '{ch}')...");
await BackgroundInput.SendKeyAsync(hWnd, vk);
await BackgroundInput.SendCharAsync(hWnd, ch);
await Task.Delay(350, cancellationToken);
}
}
}
// Gửi Space để xác nhận hoặc tắt dialog nếu còn hiển thị
await BackgroundInput.SendKeyAsync(hWnd, 0x20); // VK_SPACE
await Task.Delay(200, cancellationToken);
// 4. Đóng rương F4
await BackgroundInput.SendKeyAsync(hWnd, config.KeyBagVirtualCode);
await Task.Delay(200, cancellationToken);
log?.Invoke($"[TÚI MÁU TÂN THỦ] Đã bổ sung thành công '{config.NewbiePouchPotionName}' vào hành trang!");
return true;
}
catch (OperationCanceledException)
{
return false;
}
catch (Exception ex)
{
log?.Invoke($"[TÚI MÁU LỖI] {ex.Message}");
return false;
}
}
}