using SilkroadBot.Plugins.SDK.Attributes; using SilkroadBot.Plugins.SDK.Interfaces; using SilkroadBot.Plugins.SDK.Models; using SilkroadBot.Domain.Models; namespace SilkroadBot.Plugin.Protection; /// /// Protection plugin - monitors character safety and takes protective actions. /// Handles auto-healing, flee behavior, disconnect protection, and death recovery. /// [Plugin("protection", "Character Protection", "Auto-healing, flee behavior, and death recovery")] public class ProtectionPlugin : PluginBase { public override string Id => "protection"; public override string Name => "Character Protection"; public override Version Version => new(1, 0, 0); public override string Author => "SilkroadBot Team"; public override string Description => "Automatic character protection: healing, fleeing, and recovery"; private CancellationTokenSource? _monitorCts; // Configuration private double _healThreshold = 60.0; private double _mpPotThreshold = 30.0; private double _fleeThreshold = 15.0; private int _maxMonstersBeforeFlee = 5; private int _checkIntervalMs = 500; private bool _autoResurrect = true; private WorldPosition _safePosition; public override async Task InitializeAsync(IPluginContext context) { await base.InitializeAsync(context); await LoadConfigurationAsync(); } public override Task StartAsync(CancellationToken ct = default) { _monitorCts = CancellationTokenSource.CreateLinkedTokenSource(ct); _ = Task.Run(() => ProtectionLoopAsync(_monitorCts.Token)); return base.StartAsync(ct); } public override Task StopAsync() { _monitorCts?.Cancel(); return base.StopAsync(); } private async Task ProtectionLoopAsync(CancellationToken ct) { Log(LogLevel.Information, "Protection monitoring active"); while (!ct.IsCancellationRequested) { try { var state = Context.GetGameState(); await CheckProtectionRules(state); await Task.Delay(_checkIntervalMs, ct); } catch (OperationCanceledException) { break; } catch (Exception ex) { Log(LogLevel.Error, $"Protection check error: {ex.Message}"); await Task.Delay(2000, ct); } } } private async Task CheckProtectionRules(GameStateSnapshot state) { // Rule 1: Death recovery if (state.IsDead) { await HandleDeath(state); return; } // Rule 2: Critical HP - Flee if (state.HPPercentage < _fleeThreshold && state.IsInCombat) { await HandleFlee(state); return; } // Rule 3: Low HP - Heal if (state.HPPercentage < _healThreshold) { await HandleHeal(state); } // Rule 4: Low MP - Potion if (state.MPPercentage < _mpPotThreshold) { await HandleMPRestore(state); } // Rule 5: Too many monsters if (state.NearbyEntityCount > _maxMonstersBeforeFlee && state.IsInCombat) { Log(LogLevel.Warning, $"Too many enemies nearby ({state.NearbyEntityCount}). Consider fleeing."); } } private async Task HandleDeath(GameStateSnapshot state) { Log(LogLevel.Warning, "Character is dead!"); if (_autoResurrect) { Log(LogLevel.Information, "Attempting auto-resurrect..."); // TODO: Send resurrect packet (nearest spawn point) await Task.Delay(3000); } } private async Task HandleFlee(GameStateSnapshot state) { Log(LogLevel.Warning, $"HP critically low ({state.HPPercentage:F1}%)! Fleeing to safety..."); // Use navigation to flee to safe position var nav = Context.NavigationService; if (nav != null) { var path = await nav.FindPathAsync(state.Position, _safePosition); if (path.Count > 0) { Log(LogLevel.Information, $"Flee path found ({path.Count} waypoints)"); // TODO: Execute movement along path } } } private async Task HandleHeal(GameStateSnapshot state) { Log(LogLevel.Debug, $"HP below threshold ({state.HPPercentage:F1}%). Using health potion..."); // TODO: Send use HP potion packet await Task.CompletedTask; } private async Task HandleMPRestore(GameStateSnapshot state) { Log(LogLevel.Debug, $"MP below threshold ({state.MPPercentage:F1}%). Using MP potion..."); // TODO: Send use MP potion packet await Task.CompletedTask; } private Task LoadConfigurationAsync() { _healThreshold = Context.Configuration.Get("healThreshold", 60.0); _mpPotThreshold = Context.Configuration.Get("mpPotThreshold", 30.0); _fleeThreshold = Context.Configuration.Get("fleeThreshold", 15.0); _maxMonstersBeforeFlee = Context.Configuration.Get("maxMonsters", 5); _checkIntervalMs = Context.Configuration.Get("checkIntervalMs", 500); _autoResurrect = Context.Configuration.Get("autoResurrect", true); return Task.CompletedTask; } }