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; } } }