using System; using System.Threading; using System.Threading.Tasks; using JXHelper.Config; using JXHelper.Platform; namespace JXHelper.Core; /// /// Động cơ phục hồi sinh lực & nội lực (Recovery Engine - Học tập theo Tab P.hồi của AutoVLBS). /// Chuyên trách: Bơm máu 2 mức, bơm mana 2 mức, chuông cảnh báo an toàn. /// Tuân thủ nghiêm ngặt bất biến: INV-BG-INPUT-NO-HIJACK. /// public class RecoveryEngine : IDisposable { private CancellationTokenSource? _cts; private Task? _recoveryLoopTask; private DateTime _lastHp1Time = DateTime.MinValue; private DateTime _lastHp2Time = DateTime.MinValue; private DateTime _lastMp1Time = DateTime.MinValue; private DateTime _lastMp2Time = DateTime.MinValue; private DateTime _lastNewbiePouchTime = DateTime.MinValue; 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(); _recoveryLoopTask = Task.Run(() => RecoveryLoopAsync(hWnd, config, _cts.Token)); OnLog?.Invoke("[P.HỒI] Động cơ phục hồi máu/mana đã khởi động."); } public void Stop() { if (!IsRunning) return; IsRunning = false; IsPaused = false; _cts?.Cancel(); try { _recoveryLoopTask?.Wait(500); } catch { // Bỏ qua lỗi hủy task } _cts?.Dispose(); _cts = null; _recoveryLoopTask = null; OnLog?.Invoke("[P.HỒI] Đã dừng động cơ phục hồi."); } private async Task RecoveryLoopAsync(IntPtr hWnd, BotConfig config, CancellationToken token) { while (!token.IsCancellationRequested) { try { if (!IsPaused) { DateTime now = DateTime.Now; // 1. Bơm Sinh Lực mức 1 if (config.HpPump1Enabled && (now - _lastHp1Time).TotalMilliseconds >= config.HpPump1DelayMs) { await BackgroundInput.SendKeyAsync(hWnd, config.HpPump1Key); _lastHp1Time = now; } // 2. Bơm Sinh Lực mức 2 if (config.HpPump2Enabled && (now - _lastHp2Time).TotalMilliseconds >= config.HpPump2DelayMs) { await BackgroundInput.SendKeyAsync(hWnd, config.HpPump2Key); _lastHp2Time = now; } // 3. Bơm Nội Lực mức 1 if (config.MpPump1Enabled && (now - _lastMp1Time).TotalMilliseconds >= config.MpPump1DelayMs) { await BackgroundInput.SendKeyAsync(hWnd, config.MpPump1Key); _lastMp1Time = now; } // 4. Bơm Nội Lực mức 2 if (config.MpPump2Enabled && (now - _lastMp2Time).TotalMilliseconds >= config.MpPump2DelayMs) { await BackgroundInput.SendKeyAsync(hWnd, config.MpPump2Key); _lastMp2Time = now; } // 5. Tự động sử dụng Túi Máu Tân Thủ (Chuẩn Auto Kim Yến) if (config.AutoNewbiePouchEnabled) { bool needPouch = false; int intervalSec = Math.Max(15, config.NewbiePouchIntervalSeconds); if ((now - _lastNewbiePouchTime).TotalSeconds >= intervalSec) { needPouch = true; } else if ((now - _lastNewbiePouchTime).TotalSeconds >= 10) { // Kiểm tra khẩn cấp qua RAM: HP tụt thấp < NewbiePouchEmergencyHpPercent NativeMethods.GetWindowThreadProcessId(hWnd, out uint pid); var stats = PlayerStatsReader.ReadLiveStats((int)pid); if (stats != null && stats.MaxHp > 0 && stats.HpPercent < config.NewbiePouchEmergencyHpPercent) { needPouch = true; OnLog?.Invoke($"[TÚI MÁU KHẨN CẤP] Sinh lực còn {stats.HpPercent:F1}% (< {config.NewbiePouchEmergencyHpPercent}%), kích hoạt bổ sung máu ngay!"); } } if (needPouch) { _lastNewbiePouchTime = now; await ActionExecutor.ExecuteUseNewbiePouchAsync(hWnd, config, msg => OnLog?.Invoke(msg), token); } } } await Task.Delay(200, token); } catch (OperationCanceledException) { break; } catch (Exception ex) { OnLog?.Invoke($"[P.HỒI LỖI] {ex.Message}"); await Task.Delay(1000, token); } } } public static void PlayTdpAlarm() { try { System.Media.SystemSounds.Exclamation.Play(); } catch { // Bỏ qua lỗi âm thanh nếu hệ thống tắt loa } } public void Dispose() { Stop(); } }