File size: 2,838 Bytes
5797d5e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
using SilkroadBot.Domain.Models;

namespace SilkroadBot.Navigation.Interfaces;

/// <summary>
/// Abstract pathfinding algorithm interface.
/// Allows NavMesh, A*, Dijkstra, or any other algorithm to be plugged in.
/// </summary>
public interface IPathfindingAlgorithm
{
    /// <summary>Algorithm name.</summary>
    string Name { get; }
    
    /// <summary>Find a path between two world positions.</summary>
    Task<PathResult> FindPathAsync(WorldPosition start, WorldPosition end, PathfindingOptions? options = null);
    
    /// <summary>Check if a position is traversable.</summary>
    bool IsPositionWalkable(WorldPosition position);
    
    /// <summary>Get the nearest walkable position to the given point.</summary>
    WorldPosition GetNearestWalkablePosition(WorldPosition position);
}

/// <summary>
/// Map data provider interface for loading navigation data.
/// </summary>
public interface IMapDataProvider
{
    /// <summary>Load navigation data for a region.</summary>
    Task<RegionNavData?> LoadRegionAsync(short regionId);
    
    /// <summary>Check if navigation data is available for a region.</summary>
    bool HasRegion(short regionId);
    
    /// <summary>Get all available region IDs.</summary>
    IReadOnlyList<short> GetAvailableRegions();
}

/// <summary>
/// Navigation data for a single map region.
/// </summary>
public class RegionNavData
{
    public short RegionId { get; init; }
    public int Width { get; init; }
    public int Height { get; init; }
    public byte[,] WalkabilityGrid { get; init; } = new byte[0, 0];
    public List<NavNode> Nodes { get; init; } = new();
    public List<NavEdge> Edges { get; init; } = new();
}

/// <summary>
/// A node in the navigation graph.
/// </summary>
public record NavNode(int Id, float X, float Y, float Z, bool IsWalkable = true);

/// <summary>
/// An edge connecting two navigation nodes.
/// </summary>
public record NavEdge(int FromNodeId, int ToNodeId, float Cost);

/// <summary>
/// Result of a pathfinding operation.
/// </summary>
public record PathResult
{
    public bool Success { get; init; }
    public IReadOnlyList<WorldPosition> Waypoints { get; init; } = Array.Empty<WorldPosition>();
    public double TotalDistance { get; init; }
    public TimeSpan ComputeTime { get; init; }
    public string? ErrorMessage { get; init; }
    
    public static PathResult Failed(string reason) => new() { Success = false, ErrorMessage = reason };
}

/// <summary>
/// Options for pathfinding calculations.
/// </summary>
public record PathfindingOptions
{
    public bool AllowDiagonal { get; init; } = true;
    public double MaxDistance { get; init; } = double.MaxValue;
    public int MaxIterations { get; init; } = 10000;
    public bool SmoothPath { get; init; } = true;
    public double WaypointSpacing { get; init; } = 10.0;
}