// // WalkInRangeOperation.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.Extensions.Combat.Errors; using NosSmooth.Extensions.Pathfinding; using NosSmooth.Game.Data.Entities; using NosSmooth.Game.Data.Info; using Remora.Results; namespace NosSmooth.Extensions.Combat.Operations; /// /// A combat operation that walks into a given range of an entity. /// /// The walk manager. /// The entity to walk to. /// The maximal distance from the entity. public record WalkInRangeOperation ( WalkManager WalkManager, IEntity Entity, float Distance ) : ICombatOperation { /// public Result CanBeUsed(ICombatState combatState) { var character = combatState.Game.Character; if (character is null) { return new CharacterNotInitializedError(); } return character.CantMove ? CanBeUsedResponse.MustWait : CanBeUsedResponse.CanBeUsed; } /// public async Task UseAsync(ICombatState combatState, CancellationToken ct = default) { var character = combatState.Game.Character; if (character is null) { return new CharacterNotInitializedError(); } var distance = Distance; while (distance >= 1) { var position = Entity.Position; if (position is null) { return new GenericError("Entity's position is not initialized."); } var currentPosition = character.Position; if (currentPosition is null) { return new CharacterNotInitializedError("Position"); } var closePosition = GetClosePosition(currentPosition.Value, position.Value, distance); var walkResult = await WalkManager.GoToAsync(closePosition.X, closePosition.Y, ct); if (!walkResult.IsSuccess && walkResult.Error is NotFoundError) { distance--; continue; } return walkResult; } return Result.FromSuccess(); } private Position GetClosePosition(Position start, Position target, double distance) { var diff = start - target; var diffLength = Math.Sqrt(diff.DistanceSquared(Position.Zero)); return target + ((distance / diffLength) * diff); } }