| using SilkroadBot.Navigation.Interfaces; |
|
|
| namespace SilkroadBot.Navigation.Models; |
|
|
| |
| |
| |
| public class FileMapDataProvider : IMapDataProvider |
| { |
| private readonly string _dataDirectory; |
| private readonly Dictionary<short, RegionNavData> _cache = new(); |
| private readonly HashSet<short> _availableRegions = new(); |
|
|
| public FileMapDataProvider(string dataDirectory) |
| { |
| _dataDirectory = dataDirectory; |
| ScanAvailableRegions(); |
| } |
|
|
| public async Task<RegionNavData?> LoadRegionAsync(short regionId) |
| { |
| if (_cache.TryGetValue(regionId, out var cached)) |
| return cached; |
|
|
| var filePath = Path.Combine(_dataDirectory, $"region_{regionId}.nav"); |
| if (!File.Exists(filePath)) |
| { |
| |
| return GenerateDefaultRegion(regionId); |
| } |
|
|
| var data = await LoadFromFileAsync(filePath, regionId); |
| if (data != null) |
| _cache[regionId] = data; |
| return data; |
| } |
|
|
| public bool HasRegion(short regionId) => _availableRegions.Contains(regionId); |
|
|
| public IReadOnlyList<short> GetAvailableRegions() => _availableRegions.ToList(); |
|
|
| private void ScanAvailableRegions() |
| { |
| if (!Directory.Exists(_dataDirectory)) |
| { |
| Directory.CreateDirectory(_dataDirectory); |
| return; |
| } |
|
|
| foreach (var file in Directory.GetFiles(_dataDirectory, "region_*.nav")) |
| { |
| var name = Path.GetFileNameWithoutExtension(file); |
| if (short.TryParse(name.Replace("region_", ""), out var id)) |
| _availableRegions.Add(id); |
| } |
| } |
|
|
| private static async Task<RegionNavData?> LoadFromFileAsync(string path, short regionId) |
| { |
| try |
| { |
| var bytes = await File.ReadAllBytesAsync(path); |
| if (bytes.Length < 8) return null; |
|
|
| int width = BitConverter.ToInt32(bytes, 0); |
| int height = BitConverter.ToInt32(bytes, 4); |
| |
| var grid = new byte[width, height]; |
| int offset = 8; |
| |
| for (int x = 0; x < width && offset < bytes.Length; x++) |
| for (int y = 0; y < height && offset < bytes.Length; y++) |
| grid[x, y] = bytes[offset++]; |
|
|
| return new RegionNavData |
| { |
| RegionId = regionId, |
| Width = width * 10, |
| Height = height * 10, |
| WalkabilityGrid = grid |
| }; |
| } |
| catch |
| { |
| return null; |
| } |
| } |
|
|
| private static RegionNavData GenerateDefaultRegion(short regionId) |
| { |
| const int size = 128; |
| var grid = new byte[size, size]; |
| |
| |
| for (int x = 0; x < size; x++) |
| for (int y = 0; y < size; y++) |
| grid[x, y] = 1; |
|
|
| return new RegionNavData |
| { |
| RegionId = regionId, |
| Width = size * 10, |
| Height = size * 10, |
| WalkabilityGrid = grid |
| }; |
| } |
| } |
|
|
| |
| |
| |
| public class NavigationService : Plugins.SDK.Interfaces.INavigationService |
| { |
| private readonly Interfaces.IPathfindingAlgorithm _pathfinder; |
|
|
| public NavigationService(Interfaces.IPathfindingAlgorithm pathfinder) |
| { |
| _pathfinder = pathfinder; |
| } |
|
|
| public async Task<IReadOnlyList<Domain.Models.WorldPosition>> FindPathAsync( |
| Domain.Models.WorldPosition from, |
| Domain.Models.WorldPosition to, |
| CancellationToken ct = default) |
| { |
| var result = await _pathfinder.FindPathAsync(from, to); |
| return result.Success ? result.Waypoints : Array.Empty<Domain.Models.WorldPosition>(); |
| } |
|
|
| public bool IsWalkable(Domain.Models.WorldPosition position) |
| => _pathfinder.IsPositionWalkable(position); |
|
|
| public Domain.Models.WorldPosition GetNearestWalkable(Domain.Models.WorldPosition position) |
| => _pathfinder.GetNearestWalkablePosition(position); |
| } |
|
|