A Core/NosSmooth.Core/Commands/Walking/WalkCommand.cs => Core/NosSmooth.Core/Commands/Walking/WalkCommand.cs +32 -0
@@ 0,0 1,32 @@
+//
+// WalkCommand.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.Commands.Control;
+
+namespace NosSmooth.Core.Commands.Walking;
+
+/// <summary>
+/// Walk player and pets simultaneously.
+/// </summary>
+/// <param name="TargetX">The target x coordinate.</param>
+/// <param name="TargetY">The target y coordinate.</param>
+/// <param name="PetSelectors">The pet indices.</param>
+/// <param name="CanBeCancelledByAnother"></param>
+/// <param name="WaitForCancellation"></param>
+/// <param name="AllowUserCancel"></param>
+public record WalkCommand
+(
+ ushort TargetX,
+ ushort TargetY,
+ int[] PetSelectors,
+ bool CanBeCancelledByAnother = true,
+ bool WaitForCancellation = true,
+ bool AllowUserCancel = true
+) : ITakeControlCommand
+{
+ /// <inheritdoc />
+ public bool CancelOnMapChange => true;
+}<
\ No newline at end of file
A Core/NosSmooth.Core/Commands/Walking/WalkCommandHandler.cs => Core/NosSmooth.Core/Commands/Walking/WalkCommandHandler.cs +103 -0
@@ 0,0 1,103 @@
+//
+// WalkCommandHandler.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 System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using NosSmooth.Core.Client;
+using NosSmooth.Core.Extensions;
+using Remora.Results;
+
+namespace NosSmooth.Core.Commands.Walking;
+
+/// <summary>
+/// Handles <see cref="WalkCommand"/>.
+/// </summary>
+public class WalkCommandHandler : ICommandHandler<WalkCommand>
+{
+ private readonly INostaleClient _nostaleClient;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="WalkCommandHandler"/> class.
+ /// </summary>
+ /// <param name="nostaleClient">The NosTale client.</param>
+ public WalkCommandHandler(INostaleClient nostaleClient)
+ {
+ _nostaleClient = nostaleClient;
+ }
+
+ /// <inheritdoc />
+ public async Task<Result> HandleCommand(WalkCommand command, CancellationToken ct = default)
+ {
+ var tasks = new List<Task<Result>>();
+ for (var i = 0; i < command.PetSelectors.Length; i++)
+ {
+ short xOffset = (short)(-1 + (i % 3));
+ short yOffset = (short)(-1 + ((i / 3) % 5));
+ if (xOffset == 0 && yOffset == 0)
+ {
+ yOffset += 2;
+ }
+
+ int x = command.TargetX;
+ int y = command.TargetY;
+
+ if (x + xOffset > 0)
+ {
+ x += xOffset;
+ }
+ if (y + yOffset > 0)
+ {
+ y += yOffset;
+ }
+
+ tasks.Add
+ (
+ _nostaleClient.SendCommandAsync
+ (
+ new PetWalkCommand
+ (
+ command.PetSelectors[i],
+ (ushort)x,
+ (ushort)y,
+ command.CanBeCancelledByAnother,
+ command.WaitForCancellation,
+ command.AllowUserCancel
+ ),
+ ct
+ )
+ );
+ }
+
+ tasks.Add
+ (
+ _nostaleClient.SendCommandAsync
+ (
+ new PlayerWalkCommand
+ (
+ command.TargetX,
+ command.TargetY,
+ command.CanBeCancelledByAnother,
+ command.WaitForCancellation,
+ command.AllowUserCancel
+ ),
+ ct
+ )
+ );
+
+ var results = await Task.WhenAll(tasks);
+ var errorResults = results.Where(x => !x.IsSuccess).ToArray();
+
+ return errorResults.Length switch
+ {
+ 0 => Result.FromSuccess(),
+ 1 => errorResults[0],
+ _ => new AggregateError(errorResults.Cast<IResult>().ToArray())
+ };
+ }
+}<
\ No newline at end of file
M Core/NosSmooth.Core/Extensions/ServiceCollectionExtensions.cs => Core/NosSmooth.Core/Extensions/ServiceCollectionExtensions.cs +3 -1
@@ 10,6 10,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using NosSmooth.Core.Commands;
using NosSmooth.Core.Commands.Control;
+using NosSmooth.Core.Commands.Walking;
using NosSmooth.Core.Packets;
using NosSmooth.Packets.Extensions;
@@ 40,7 41,7 @@ public static class ServiceCollectionExtensions
}
/// <summary>
- /// Adds command handling of <see cref="TakeControlCommand"/>.
+ /// Adds command handling of <see cref="TakeControlCommand"/> and <see cref="WalkCommand"/>.
/// </summary>
/// <param name="serviceCollection">The service collection to register the responder to.</param>
/// <returns>The collection.</returns>
@@ 49,6 50,7 @@ public static class ServiceCollectionExtensions
return serviceCollection
.AddSingleton<ControlCommands>()
.AddPacketResponder<ControlCommandPacketResponders>()
+ .AddCommandHandler<WalkCommandHandler>()
.AddCommandHandler<TakeControlCommandHandler>();
}