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