// // WalkManager.cs // // Copyright (c) František Boháček. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using NosSmooth.Core.Client; using NosSmooth.Core.Commands.Walking; using NosSmooth.Core.Errors; using NosSmooth.Extensions.Pathfinding.Errors; using Remora.Results; namespace NosSmooth.Extensions.Pathfinding; /// /// The walk manager using pathfinding to walk to given position. /// public class WalkManager { private readonly INostaleClient _client; private readonly Pathfinder _pathfinder; private readonly PathfinderState _state; /// /// Initializes a new instance of the class. /// /// The client. /// The pathfinder. /// The state. public WalkManager(INostaleClient client, Pathfinder pathfinder, PathfinderState state) { _client = client; _pathfinder = pathfinder; _state = state; } /// /// Move character to the given position. /// /// /// Expect if the destination could not be reached. /// Expect if the path could not be found. /// /// The target x coordinate. /// The target y coordinate. /// Whether to allow user actions during the walk operation. /// The cancellation token used for cancelling the operation. /// A result that may not succeed. public async Task PlayerGoToAsync ( short x, short y, bool allowUserActions = true, CancellationToken ct = default ) { var pathResult = _pathfinder.FindPathFromCurrent(x, y); if (!pathResult.IsDefined(out var path)) { return Result.FromError(pathResult); } return await TakePath ( path, _state.Character, (x, y) => _client.SendCommandAsync ( new WalkCommand ( x, y, 2, AllowUserCancel: allowUserActions ), ct ) ); } /// /// Move pet to the given position. /// /// /// Expect if the destination could not be reached. /// Expect if the path could not be found. /// /// The id of the mate to move. /// The target x coordinate. /// The target y coordinate. /// Whether to allow user actions during the walk operation. /// The cancellation token used for cancelling the operation. /// A result that may not succeed. public async Task MateWalkToAsync ( long mateId, short x, short y, bool allowUserActions = true, CancellationToken ct = default ) { if (!_state.Entities.TryGetValue(mateId, out var entityState) || entityState == _state.Character) { return new EntityStateNotFoundError(mateId); } var pathResult = _pathfinder.FindPathFromEntity(mateId, x, y); if (!pathResult.IsDefined(out var path)) { return Result.FromError(pathResult); } return await TakePath ( path, entityState, (x, y) => _client.SendCommandAsync ( new MateWalkCommand ( mateId, x, y, 2, AllowUserCancel: allowUserActions ), ct ) ); } private async Task TakePath(Path path, EntityState state, Func> walkFunc) { if (path.Parts.Count == 0) { return Result.FromSuccess(); } var target = path.Parts.Last(); while (!path.ReachedEnd) { if (path.MapId != _state.MapId) { return new WalkNotFinishedError(state.X, state.Y, WalkUnfinishedReason.MapChanged); } var next = path.TakeForwardPath(); var walkResult = await walkFunc(next.X, next.Y); if (!walkResult.IsSuccess) { if (path.ReachedEnd && walkResult.Error is WalkNotFinishedError walkNotFinishedError && walkNotFinishedError.Reason == WalkUnfinishedReason.MapChanged) { return Result.FromSuccess(); } if (state.X == target.X && state.Y == target.Y) { return Result.FromSuccess(); } return walkResult; } } return Result.FromSuccess(); } }