| using SilkroadBot.Plugins.SDK.Attributes; |
| using SilkroadBot.Plugins.SDK.Interfaces; |
| using SilkroadBot.Plugins.SDK.Models; |
| using SilkroadBot.Domain.Models; |
|
|
| namespace SilkroadBot.Plugin.Protection; |
|
|
| |
| |
| |
| |
| [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; |
| |
| |
| 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) |
| { |
| |
| if (state.IsDead) |
| { |
| await HandleDeath(state); |
| return; |
| } |
|
|
| |
| if (state.HPPercentage < _fleeThreshold && state.IsInCombat) |
| { |
| await HandleFlee(state); |
| return; |
| } |
|
|
| |
| if (state.HPPercentage < _healThreshold) |
| { |
| await HandleHeal(state); |
| } |
|
|
| |
| if (state.MPPercentage < _mpPotThreshold) |
| { |
| await HandleMPRestore(state); |
| } |
|
|
| |
| 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..."); |
| |
| await Task.Delay(3000); |
| } |
| } |
|
|
| private async Task HandleFlee(GameStateSnapshot state) |
| { |
| Log(LogLevel.Warning, $"HP critically low ({state.HPPercentage:F1}%)! Fleeing to safety..."); |
| |
| |
| 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)"); |
| |
| } |
| } |
| } |
|
|
| private async Task HandleHeal(GameStateSnapshot state) |
| { |
| Log(LogLevel.Debug, $"HP below threshold ({state.HPPercentage:F1}%). Using health potion..."); |
| |
| await Task.CompletedTask; |
| } |
|
|
| private async Task HandleMPRestore(GameStateSnapshot state) |
| { |
| Log(LogLevel.Debug, $"MP below threshold ({state.MPPercentage:F1}%). Using MP potion..."); |
| |
| 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; |
| } |
| } |
|
|