File size: 5,389 Bytes
cd011a2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | using SilkroadBot.Plugins.SDK.Attributes;
using SilkroadBot.Plugins.SDK.Interfaces;
using SilkroadBot.Plugins.SDK.Models;
using SilkroadBot.Domain.Models;
namespace SilkroadBot.Plugin.Protection;
/// <summary>
/// Protection plugin - monitors character safety and takes protective actions.
/// Handles auto-healing, flee behavior, disconnect protection, and death recovery.
/// </summary>
[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;
}
}
|