@@ 0,0 1,107 @@
+//
+// NostaleChatPacketApi.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 NosCore.Packets.Enumerations;
+using NosCore.Packets.ServerPackets.Chats;
+using NosSmooth.Core.Client;
+using NosSmooth.Game.Data.Entities;
+using Remora.Results;
+
+namespace NosSmooth.Game.Apis;
+
+/// <summary>
+/// Packet api for sending and receiving messages.
+/// </summary>
+public class NostaleChatPacketApi
+{
+ // TODO: check length of the messages
+ private readonly INostaleClient _client;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NostaleChatPacketApi"/> class.
+ /// </summary>
+ /// <param name="client">The nostale client.</param>
+ public NostaleChatPacketApi(INostaleClient client)
+ {
+ _client = client;
+ }
+
+ /// <summary>
+ /// Receive the given system message on the client.
+ /// </summary>
+ /// <remarks>
+ /// Won't send anything to the server, it's just the client who will see the message.
+ /// </remarks>
+ /// <param name="content">The content of the message.</param>
+ /// <param name="color">The color of the message.</param>
+ /// <param name="ct">The cancellation token for cancelling the operation.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public Task<Result> ReceiveSystemMessageAsync(string content, SayColorType color = SayColorType.Yellow, CancellationToken ct = default)
+ {
+ return _client.ReceivePacketAsync(
+ new SayPacket
+ {
+ Message = content, Type = color
+ },
+ ct
+ );
+ }
+
+ /// <summary>
+ /// Sends the given message to the public chat.
+ /// </summary>
+ /// <param name="content">The content of the message.</param>
+ /// <param name="ct">The cancellation token for cancelling the operation.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public Task<Result> SendMessageAsync(string content, CancellationToken ct = default)
+ {
+ return _client.SendPacketAsync(
+ new SayPacket
+ {
+ Message = content
+ },
+ ct
+ );
+ }
+
+ /// <summary>
+ /// Sends the given message to the family chat.
+ /// </summary>
+ /// <remarks>
+ /// Should be used only if the user is in a family.
+ /// </remarks>
+ /// <param name="content">The content of the message.</param>
+ /// <param name="ct">The cancellation token for cancelling the operation.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public Task<Result> SendFamilyMessage(string content, CancellationToken ct = default)
+ => _client.SendPacketAsync(":" + content, ct);
+
+ /// <summary>
+ /// Sends the given message to the group chat.
+ /// </summary>
+ /// <remarks>
+ /// Should be used only if the user is in a group. (with people, not only pets).
+ /// </remarks>
+ /// <param name="content">The content of the message.</param>
+ /// <param name="ct">The cancellation token for cancelling the operation.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public Task<Result> SendGroupMessage(string content, CancellationToken ct = default)
+ => _client.SendPacketAsync(";" + content, ct);
+
+ /// <summary>
+ /// Sends the given message to the target only.
+ /// </summary>
+ /// <remarks>
+ /// Won't return if the whisper has actually came through, event has to be hooked
+ /// up to know if the whisper has went through (and you can know only for messages that are sufficiently long).
+ /// </remarks>
+ /// <param name="targetName">The name of the user you want to whisper to.</param>
+ /// <param name="content">The content of the message.</param>
+ /// <param name="ct">The cancellation token for cancelling the operation.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public Task<Result> SendWhisper(string targetName, string content, CancellationToken ct = default)
+ => _client.SendPacketAsync($"/{targetName} {content}", ct);
+}<
\ No newline at end of file
@@ 0,0 1,182 @@
+//
+// NostaleSkillsPacketApi.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 NosCore.Packets.ClientPackets.Battle;
+using NosCore.Shared.Enumerations;
+using NosSmooth.Core.Client;
+using NosSmooth.Game.Data.Characters;
+using NosSmooth.Game.Data.Entities;
+using NosSmooth.Game.Errors;
+using Remora.Results;
+
+namespace NosSmooth.Game.Apis;
+
+/// <summary>
+/// Packet api for using character skills.
+/// </summary>
+public class NostaleSkillsPacketApi
+{
+ private readonly INostaleClient _client;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NostaleSkillsPacketApi"/> class.
+ /// </summary>
+ /// <param name="client">The nostale client.</param>
+ public NostaleSkillsPacketApi(INostaleClient client)
+ {
+ _client = client;
+ }
+
+ /// <summary>
+ /// Use the given (targetable) skill on specified entity.
+ /// </summary>
+ /// <remarks>
+ /// For skills that can be used only on self, use <paramref name="entityId"/> of the character.
+ /// For skills that cannot be targeted on an entity, proceed to <see cref="UseSkillAt"/>.
+ /// </remarks>
+ /// <param name="skillVNum">The id of the skill.</param>
+ /// <param name="entityId">The id of the entity to use the skill on.</param>
+ /// <param name="entityType">The type of the supplied entity.</param>
+ /// <param name="mapX">The x coordinate on the map. (Used for non targeted dashes etc., says where the dash will be to.)</param>
+ /// <param name="mapY">The y coordinate on the map. (Used for non targeted dashes etc., says where the dash will be to.)</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public Task<Result> UseSkillOn(long skillVNum, long entityId, VisualType entityType, short? mapX = default, short? mapY = default)
+ {
+ return _client.SendPacketAsync(new UseSkillPacket
+ {
+ CastId = skillVNum,
+ MapX = mapX,
+ MapY = mapY,
+ TargetId = entityId,
+ TargetVisualType = entityType
+ });
+ }
+
+ /// <summary>
+ /// Use the given (targetable) skill on specified entity.
+ /// </summary>
+ /// <remarks>
+ /// For skills that can be used only on self, use <paramref name="entityId"/> of the character.
+ /// For skills that cannot be targeted on an entity, proceed to <see cref="UseSkillAt"/>.
+ /// </remarks>
+ /// <param name="skillVNum">The id of the skill.</param>
+ /// <param name="entity">The entity to use the skill on.</param>
+ /// <param name="mapX">The x coordinate on the map. (Used for non targeted dashes etc., says where the dash will be to.)</param>
+ /// <param name="mapY">The y coordinate on the map. (Used for non targeted dashes etc., says where the dash will be to.)</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public Task<Result> UseSkillOn(long skillVNum, ILivingEntity entity, short? mapX = default, short? mapY = default)
+ {
+ return _client.SendPacketAsync(new UseSkillPacket
+ {
+ CastId = skillVNum,
+ MapX = mapX,
+ MapY = mapY,
+ TargetId = entity.Id,
+ TargetVisualType = entity.Type
+ });
+ }
+
+ /// <summary>
+ /// Use the given (targetable) skill on specified entity.
+ /// </summary>
+ /// <remarks>
+ /// The skill won't be used if it is on cooldown.
+ /// For skills that can be used only on self, use <paramref name="entityId"/> of the character.
+ /// For skills that cannot be targeted on an entity, proceed to <see cref="UseSkillAt"/>.
+ /// </remarks>
+ /// <param name="skill">The skill to use.</param>
+ /// <param name="entity">The entity to use the skill on.</param>
+ /// <param name="mapX">The x coordinate on the map. (Used for non targeted dashes etc., says where the dash will be to.)</param>
+ /// <param name="mapY">The y coordinate on the map. (Used for non targeted dashes etc., says where the dash will be to.)</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public Task<Result> UseSkillOn(Skill skill, ILivingEntity entity, short? mapX = default, short? mapY = default)
+ {
+ if (skill.IsOnCooldown)
+ {
+ return Task.FromResult<Result>(new SkillOnCooldownError(skill));
+ }
+
+ return _client.SendPacketAsync(new UseSkillPacket
+ {
+ CastId = skill.SkillVNum,
+ MapX = mapX,
+ MapY = mapY,
+ TargetId = entity.Id,
+ TargetVisualType = entity.Type
+ });
+ }
+
+ /// <summary>
+ /// Use the given (targetable) skill on specified entity.
+ /// </summary>
+ /// <remarks>
+ /// For skills that can be used only on self, use <paramref name="entityId"/> of the character.
+ /// For skills that cannot be targeted on an entity, proceed to <see cref="UseSkillAt"/>.
+ /// </remarks>
+ /// <param name="skill">The skill to use.</param>
+ /// <param name="entityId">The id of the entity to use the skill on.</param>
+ /// <param name="entityType">The type of the supplied entity.</param>
+ /// <param name="mapX">The x coordinate on the map. (Used for non targeted dashes etc., says where the dash will be to.)</param>
+ /// <param name="mapY">The y coordinate on the map. (Used for non targeted dashes etc., says where the dash will be to.)</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public Task<Result> UseSkillOn(Skill skill, long entityId, VisualType entityType, short? mapX = default, short? mapY = default)
+ {
+ if (skill.IsOnCooldown)
+ {
+ return Task.FromResult<Result>(new SkillOnCooldownError(skill));
+ }
+
+ return _client.SendPacketAsync(new UseSkillPacket
+ {
+ CastId = skill.SkillVNum,
+ MapX = mapX,
+ MapY = mapY,
+ TargetId = entityId,
+ TargetVisualType = entityType
+ });
+ }
+
+ /// <summary>
+ /// Use the given (aoe) skill on the specified place.
+ /// </summary>
+ /// <remarks>
+ /// For skills that can have targets, proceed to <see cref="UseSkillOn"/>.
+ /// </remarks>
+ /// <param name="skillVNum">The id of the skill.</param>
+ /// <param name="mapX">The x coordinate to use the skill at.</param>
+ /// <param name="mapY">The y coordinate to use the skill at.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public Task<Result> UseSkillAt(long skillVNum, short mapX, short mapY)
+ {
+ return _client.SendPacketAsync(new UseAoeSkillPacket
+ {
+ CastId = skillVNum, MapX = mapX, MapY = mapY
+ });
+ }
+
+ /// <summary>
+ /// Use the given (aoe) skill on the specified place.
+ /// </summary>
+ /// <remarks>
+ /// For skills that can have targets, proceed to <see cref="UseSkillOn"/>.
+ /// </remarks>
+ /// <param name="skill">The skill to use.</param>
+ /// <param name="mapX">The x coordinate to use the skill at.</param>
+ /// <param name="mapY">The y coordinate to use the skill at.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public Task<Result> UseSkillAt(Skill skill, short mapX, short mapY)
+ {
+ if (skill.IsOnCooldown)
+ {
+ return Task.FromResult<Result>(new SkillOnCooldownError(skill));
+ }
+
+ return _client.SendPacketAsync(new UseAoeSkillPacket
+ {
+ CastId = skill.SkillVNum, MapX = mapX, MapY = mapY
+ });
+ }
+}<
\ No newline at end of file
@@ 9,6 9,7 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
using NosSmooth.Core.Commands;
using NosSmooth.Core.Extensions;
using NosSmooth.Game.Events.Handlers;
+using NosSmooth.Game.Apis;
namespace NosSmooth.Game.Extensions;
@@ 31,6 32,10 @@ public static class ServiceCollectionExtensions
serviceCollection.TryAddSingleton<Game>();
// TODO: add events
+
+ serviceCollection
+ .AddTransient<NostaleChatPacketApi>()
+ .AddTransient<NostaleSkillsPacketApi>();
return serviceCollection;
}