started adding navigation

This commit is contained in:
Boki 2026-02-13 10:43:35 -05:00
parent 32781b1462
commit 468e0a7246
20 changed files with 844 additions and 31 deletions

View file

@ -0,0 +1,156 @@
using Poe2Trade.Core;
using Poe2Trade.Game;
using Serilog;
namespace Poe2Trade.Navigation;
public class NavigationExecutor : IDisposable
{
private readonly IGameController _game;
private readonly MinimapConfig _config;
private readonly MinimapCapture _capture;
private readonly PositionTracker _tracker;
private readonly WorldMap _worldMap;
private NavigationState _state = NavigationState.Idle;
private bool _stopped;
private static readonly Random Rng = new();
public event Action<NavigationState>? StateChanged;
public NavigationState State => _state;
public NavigationExecutor(IGameController game, MinimapConfig? config = null)
{
_game = game;
_config = config ?? new MinimapConfig();
_capture = new MinimapCapture(_config);
_tracker = new PositionTracker(_config);
_worldMap = new WorldMap(_config);
}
private void SetState(NavigationState s)
{
_state = s;
StateChanged?.Invoke(s);
}
public Task Stop()
{
_stopped = true;
SetState(NavigationState.Idle);
Log.Information("Navigation executor stopped");
return Task.CompletedTask;
}
public void Reset()
{
_tracker.Reset();
_worldMap.Reset();
_stopped = false;
SetState(NavigationState.Idle);
}
public async Task RunExploreLoop()
{
_stopped = false;
Log.Information("Starting explore loop");
// Open minimap overlay (Tab)
await _game.ToggleMinimap();
await Helpers.Sleep(300);
while (!_stopped)
{
try
{
// 1. Capture frame
SetState(NavigationState.Capturing);
using var frame = _capture.CaptureFrame();
if (frame == null)
{
Log.Warning("Failed to capture minimap frame");
await Helpers.Sleep(200);
continue;
}
// 2. Track position via phase correlation
SetState(NavigationState.Processing);
var pos = _tracker.UpdatePosition(frame.GrayMat);
// 3. Stitch into world map
_worldMap.StitchFrame(frame.ClassifiedMat, pos);
// 4. Check if stuck
if (_tracker.IsStuck)
{
SetState(NavigationState.Stuck);
Log.Information("Stuck detected, clicking random direction");
await ClickRandomDirection();
await Helpers.Sleep(_config.MovementWaitMs);
continue;
}
// 5. Find best exploration direction
SetState(NavigationState.Planning);
var direction = _worldMap.FindNearestUnexplored(pos);
if (direction == null)
{
Log.Information("Map fully explored");
SetState(NavigationState.Completed);
break;
}
// 6. Click to move in that direction
SetState(NavigationState.Moving);
await ClickToMove(direction.Value.dirX, direction.Value.dirY);
// 7. Wait for character to walk
await Helpers.Sleep(_config.MovementWaitMs);
}
catch (Exception ex)
{
Log.Error(ex, "Error in explore loop");
SetState(NavigationState.Failed);
await Helpers.Sleep(1000);
}
}
if (_state != NavigationState.Completed)
SetState(NavigationState.Idle);
Log.Information("Explore loop ended");
}
private async Task ClickToMove(double dirX, double dirY)
{
// Player is at minimap center on screen; click offset from center
var len = Math.Sqrt(dirX * dirX + dirY * dirY);
if (len < 0.001) return;
var nx = dirX / len;
var ny = dirY / len;
var clickX = _config.MinimapCenterX + (int)(nx * _config.ClickRadius);
var clickY = _config.MinimapCenterY + (int)(ny * _config.ClickRadius);
Log.Debug("Click to move: ({X}, {Y}) dir=({Dx:F2}, {Dy:F2})", clickX, clickY, nx, ny);
await _game.LeftClickAt(clickX, clickY);
}
private async Task ClickRandomDirection()
{
var angle = Rng.NextDouble() * 2 * Math.PI;
await ClickToMove(Math.Cos(angle), Math.Sin(angle));
}
public MapPosition Position => _tracker.Position;
public byte[] GetMapSnapshot() => _worldMap.GetMapSnapshot();
public byte[] GetViewportSnapshot(int viewSize = 400) => _worldMap.GetViewportSnapshot(_tracker.Position, viewSize);
public void Dispose()
{
_capture.Dispose();
_tracker.Dispose();
_worldMap.Dispose();
}
}