A Core/NosSmooth.Game/Apis/NostaleInventoryPacketApi.cs => Core/NosSmooth.Game/Apis/NostaleInventoryPacketApi.cs +101 -0
@@ 0,0 1,101 @@
+//
+// NostaleInventoryPacketApi.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.Packets.Client.Inventory;
+using NosSmooth.Packets.Enums.Inventory;
+using Remora.Results;
+
+namespace NosSmooth.Game.Apis;
+
+/// <summary>
+/// Packet api for managing items in inventory.
+/// </summary>
+public class NostaleInventoryPacketApi
+{
+ private readonly INostaleClient _client;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NostaleInventoryPacketApi"/> class.
+ /// </summary>
+ /// <param name="client">The nostale client.</param>
+ public NostaleInventoryPacketApi(INostaleClient client)
+ {
+ _client = client;
+ }
+
+ /// <summary>
+ /// Drop the given item.
+ /// </summary>
+ /// <param name="bag">The bag where the item is located.</param>
+ /// <param name="slot">The slot the item is at.</param>
+ /// <param name="amount">The amount to drop.</param>
+ /// <param name="ct">The cancellation token used for cancelling the operation.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public async Task<Result> DropItemAsync
+ (
+ BagType bag,
+ short slot,
+ short amount,
+ CancellationToken ct = default
+ )
+ => await _client.SendPacketAsync(new PutPacket(bag, slot, amount), ct);
+
+ /// <summary>
+ /// Move the given item within one bag.
+ /// </summary>
+ /// <param name="bag">The bag the item is in.</param>
+ /// <param name="sourceSlot">The source slot to move the item from.</param>
+ /// <param name="destinationSlot">The destination slot to move the item to.</param>
+ /// <param name="amount">The amount to move.</param>
+ /// <param name="ct">The cancellation token used for cancelling the operation.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public async Task<Result> MoveItemAsync
+ (
+ BagType bag,
+ short sourceSlot,
+ short destinationSlot,
+ short amount,
+ CancellationToken ct = default
+ )
+ => await MoveItemAsync
+ (
+ bag,
+ sourceSlot,
+ bag,
+ destinationSlot,
+ amount,
+ ct
+ );
+
+ /// <summary>
+ /// Move an item from the given source bag and slot to the given destination bag and slot.
+ /// </summary>
+ /// <param name="sourceBag">The bag the item is in.</param>
+ /// <param name="sourceSlot">The source slot to move the item from.</param>
+ /// <param name="destinationBag">The destination bag to move the item to.</param>
+ /// <param name="destinationSlot">The destination slot to move the item to.</param>
+ /// <param name="amount">The amount to move.</param>
+ /// <param name="ct">The cancellation token used for cancelling the operation.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public async Task<Result> MoveItemAsync
+ (
+ BagType sourceBag,
+ short sourceSlot,
+ BagType destinationBag,
+ short destinationSlot,
+ short amount,
+ CancellationToken ct = default
+ )
+ {
+ if (sourceBag == destinationBag)
+ {
+ return await _client.SendPacketAsync(new MviPacket(sourceBag, sourceSlot, amount, destinationSlot), ct);
+ }
+
+ return await _client.SendPacketAsync(new MvePacket(sourceBag, sourceSlot, destinationBag, destinationSlot), ct);
+ }
+}<
\ No newline at end of file
A Core/NosSmooth.Game/Apis/NostaleMapPacketApi.cs => Core/NosSmooth.Game/Apis/NostaleMapPacketApi.cs +192 -0
@@ 0,0 1,192 @@
+//
+// NostaleMapPacketApi.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.Game.Attributes;
+using NosSmooth.Game.Data.Entities;
+using NosSmooth.Game.Data.Items;
+using NosSmooth.Game.Errors;
+using NosSmooth.Packets.Client.Inventory;
+using NosSmooth.Packets.Client.Movement;
+using NosSmooth.Packets.Enums.Entities;
+using Remora.Results;
+
+namespace NosSmooth.Game.Apis;
+
+/// <summary>
+/// Packet api for managing maps in inventory.
+/// </summary>
+public class NostaleMapPacketApi
+{
+ /// <summary>
+ /// The range the player may pick up items in.
+ /// </summary>
+ public static short PickUpRange => 5;
+
+ private readonly Game _game;
+ private readonly INostaleClient _client;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NostaleMapPacketApi"/> class.
+ /// </summary>
+ /// <param name="game">The game.</param>
+ /// <param name="client">The client.</param>
+ public NostaleMapPacketApi(Game game, INostaleClient client)
+ {
+ _game = game;
+ _client = client;
+ }
+
+ /// <summary>
+ /// Use the given portal.
+ /// </summary>
+ /// <param name="ct">The cancellation token for cancelling the operation.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ [Unsafe("Portal position not checked.")]
+ public async Task<Result> UsePortalAsync(CancellationToken ct = default)
+ => await _client.SendPacketAsync(new PreqPacket(), ct);
+
+ /// <summary>
+ /// Pick up the given item.
+ /// </summary>
+ /// <remarks>
+ /// Checks that the item is in distance,
+ /// if the character's position or
+ /// item's position is not initialized, returns
+ /// an error.
+ /// </remarks>
+ /// <param name="item">The item.</param>
+ /// <param name="ct">The cancellation token used for cancelling the operation.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public async Task<Result> CharacterPickUpAsync(GroundItem item, CancellationToken ct = default)
+ {
+ var character = _game.Character;
+ if (character is null)
+ {
+ return new NotInitializedError("Game.Character");
+ }
+
+ var characterPosition = character.Position;
+ if (characterPosition is null)
+ {
+ return new NotInitializedError("Game.Character.Position");
+ }
+
+ var itemPosition = item.Position;
+ if (itemPosition is null)
+ {
+ return new NotInitializedError("item.Position");
+ }
+
+ if (!itemPosition.Value.IsInRange(characterPosition.Value, PickUpRange))
+ {
+ return new NotInRangeError("Character", characterPosition.Value, itemPosition.Value, PickUpRange);
+ }
+
+ return await CharacterPickUpAsync(item.Id, ct);
+ }
+
+ /// <summary>
+ /// Pick up the given item by character.
+ /// </summary>
+ /// <remarks>
+ /// Unsafe, does not check anything.
+ /// </remarks>
+ /// <param name="itemId">The id of the item.</param>
+ /// <param name="ct">The cancellation token used for cancelling the operation.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ [Unsafe("Nor character distance, nor the existence of item is checked.")]
+ public async Task<Result> CharacterPickUpAsync(long itemId, CancellationToken ct = default)
+ {
+ var character = _game.Character;
+ if (character is null)
+ {
+ return new NotInitializedError("Character");
+ }
+
+ return await _client.SendPacketAsync(new GetPacket(EntityType.Player, character.Id, itemId), ct);
+ }
+
+ /// <summary>
+ /// Pick up the given item.
+ /// </summary>
+ /// <remarks>
+ /// Checks that the character has a pet company,
+ /// that the item is in distance.
+ /// When the pet's position or
+ /// item's position is not initialized, returns
+ /// an error.
+ /// </remarks>
+ /// <param name="item">The item.</param>
+ /// <param name="ct">The cancellation token used for cancelling the operation.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public async Task<Result> PetPickUpAsync(GroundItem item, CancellationToken ct = default)
+ {
+ var mates = _game.Mates;
+ if (mates is null)
+ {
+ return new NotInitializedError("Game.Mates");
+ }
+
+ var pet = mates.CurrentPet;
+ if (pet is null)
+ {
+ return new NotInitializedError("Game.Mates.CurrentPet");
+ }
+
+ var entity = _game.CurrentMap?.Entities.GetEntity(pet.Pet.MateId);
+ if (entity is null)
+ {
+ return new NotInitializedError("Game.CurrentMap.Entities.PetEntity");
+ }
+
+ var petPosition = entity.Position;
+ if (petPosition is null)
+ {
+ return new NotInitializedError("Game.CurrentMap.Entities.PetEntity.Position");
+ }
+
+ var itemPosition = item.Position;
+ if (itemPosition is null)
+ {
+ return new NotInitializedError("item.Position");
+ }
+
+ if (!itemPosition.Value.IsInRange(petPosition.Value, PickUpRange))
+ {
+ return new NotInRangeError("Pet", petPosition.Value, itemPosition.Value, PickUpRange);
+ }
+
+ return await PetPickUpAsync(item.Id, ct);
+ }
+
+ /// <summary>
+ /// Pick up the given item by pet.
+ /// </summary>
+ /// <remarks>
+ /// Unsafe, does not check anything.
+ /// </remarks>
+ /// <param name="itemId">The id of the item.</param>
+ /// <param name="ct">The cancellation token used for cancelling the operation.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ [Unsafe("Nor pet distance to item nor whether the item exists is checked.")]
+ public async Task<Result> PetPickUpAsync(long itemId, CancellationToken ct = default)
+ {
+ var mates = _game.Mates;
+ if (mates is null)
+ {
+ return new NotInitializedError("Game.Mates");
+ }
+
+ var pet = mates.CurrentPet;
+ if (pet is null)
+ {
+ return new NotInitializedError("Game.Mates.CurrentPet");
+ }
+
+ return await _client.SendPacketAsync(new GetPacket(EntityType.Player, pet.Pet.MateId, itemId), ct);
+ }
+}<
\ No newline at end of file
A Core/NosSmooth.Game/Apis/NostaleMatePacketApi.cs => Core/NosSmooth.Game/Apis/NostaleMatePacketApi.cs +106 -0
@@ 0,0 1,106 @@
+//
+// NostaleMatePacketApi.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.Game.Attributes;
+using NosSmooth.Packets.Client;
+using NosSmooth.Packets.Enums.Entities;
+using NosSmooth.Packets.Enums.NRun;
+using Remora.Results;
+
+namespace NosSmooth.Game.Apis;
+
+/// <summary>
+/// Packet api for managing mates, company, stay, sending them back.
+/// </summary>
+public class NostaleMatePacketApi
+{
+ private readonly INostaleClient _client;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NostaleMatePacketApi"/> class.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ public NostaleMatePacketApi(INostaleClient client)
+ {
+ _client = client;
+ }
+
+ /// <summary>
+ /// Make the given pet your company (possible only in miniland).
+ /// </summary>
+ /// <remarks>
+ /// May be used only in miniland.
+ ///
+ /// Unsafe, does not check whether the mate exists
+ /// or whether the character is in miniland.
+ /// </remarks>
+ /// <param name="mateId">The id of the mate to have company.</param>
+ /// <param name="ct">The cancellation token used for cancelling the operation.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public async Task<Result> CompanyAsync(long mateId, CancellationToken ct = default)
+ => await _client.SendPacketAsync
+ (
+ new NRunPacket
+ (
+ NRunType.Mate,
+ (short)MateNRunType.Company,
+ EntityType.Npc,
+ mateId,
+ null
+ ),
+ ct
+ );
+
+ /// <summary>
+ /// Make the given pet stay in your miniland (possible only in miniland).
+ /// </summary>
+ /// <remarks>
+ /// May be used only in miniland.
+ ///
+ /// Unsafe, does not check whether the mate exists
+ /// or whether the character is in miniland.
+ /// </remarks>
+ /// <param name="mateId">The id of the mate to have company.</param>
+ /// <param name="ct">The cancellation token used for cancelling the operation.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public async Task<Result> StayAsync(long mateId, CancellationToken ct = default)
+ => await _client.SendPacketAsync
+ (
+ new NRunPacket
+ (
+ NRunType.Mate,
+ (short)MateNRunType.Stay,
+ EntityType.Npc,
+ mateId,
+ null
+ ),
+ ct
+ );
+
+ /// <summary>
+ /// Save the given pet back to miniland.
+ /// </summary>
+ /// <remarks>
+ /// Unsafe, does not check whether the mate exists.
+ /// </remarks>
+ /// <param name="mateId">The id of the mate to have company.</param>
+ /// <param name="ct">The cancellation token used for cancelling the operation.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public async Task<Result> SendBackAsync(long mateId, CancellationToken ct = default)
+ => await _client.SendPacketAsync
+ (
+ new NRunPacket
+ (
+ NRunType.Mate,
+ (short)MateNRunType.RequestPetSendBack,
+ EntityType.Npc,
+ mateId,
+ null
+ ),
+ ct
+ );
+}<
\ No newline at end of file
A Core/NosSmooth.Game/Apis/NostaleMateSkillsPacketApi.cs => Core/NosSmooth.Game/Apis/NostaleMateSkillsPacketApi.cs +95 -0
@@ 0,0 1,95 @@
+//
+// NostaleMateSkillsPacketApi.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.Packets.Client.Battle;
+using NosSmooth.Packets.Enums.Entities;
+using Remora.Results;
+
+namespace NosSmooth.Game.Apis;
+
+/// <summary>
+/// Packet api for using mate skills.
+/// </summary>
+public class NostaleMateSkillsPacketApi
+{
+ private readonly INostaleClient _client;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NostaleMateSkillsPacketApi"/> class.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ public NostaleMateSkillsPacketApi(INostaleClient client)
+ {
+ _client = client;
+ }
+
+ /// <summary>
+ /// Use a pet skill.
+ /// </summary>
+ /// <param name="petId">The pet id.</param>
+ /// <param name="targetEntityType">The type of the target entity.</param>
+ /// <param name="targetId">The id of the target.</param>
+ /// <param name="mapX">The x coordinate of the partner.</param>
+ /// <param name="mapY">The y coordinate of the partner.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public async Task<Result> UsePetSkillAsync
+ (
+ long petId,
+ EntityType targetEntityType,
+ long targetId,
+ short? mapX = default,
+ short? mapY = default
+ )
+ {
+ return await _client.SendPacketAsync
+ (
+ new UsePetSkillPacket
+ (
+ petId,
+ targetEntityType,
+ targetId,
+ 1,
+ mapX,
+ mapY
+ )
+ );
+ }
+
+ /// <summary>
+ /// Use a partner skill.
+ /// </summary>
+ /// <param name="partnerId">The pet id.</param>
+ /// <param name="skillSlot">The slot of the skill.</param>
+ /// <param name="targetEntityType">The type of the target entity.</param>
+ /// <param name="targetId">The id of the target.</param>
+ /// <param name="mapX">The x coordinate of the partner.</param>
+ /// <param name="mapY">The y coordinate of the partner.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public async Task<Result> UsePartnerSkillAsync
+ (
+ long partnerId,
+ byte skillSlot,
+ EntityType targetEntityType,
+ long targetId,
+ short? mapX = default,
+ short? mapY = default
+ )
+ {
+ return await _client.SendPacketAsync
+ (
+ new UsePartnerSkillPacket
+ (
+ partnerId,
+ targetEntityType,
+ targetId,
+ skillSlot,
+ mapX,
+ mapY
+ )
+ );
+ }
+}<
\ No newline at end of file
M Core/NosSmooth.Game/Apis/NostaleSkillsPacketApi.cs => Core/NosSmooth.Game/Apis/NostaleSkillsPacketApi.cs +43 -1
@@ 20,14 20,17 @@ namespace NosSmooth.Game.Apis;
public class NostaleSkillsPacketApi
{
private readonly INostaleClient _client;
+ private readonly Game _game;
/// <summary>
/// Initializes a new instance of the <see cref="NostaleSkillsPacketApi"/> class.
/// </summary>
/// <param name="client">The nostale client.</param>
- public NostaleSkillsPacketApi(INostaleClient client)
+ /// <param name="game">The game.</param>
+ public NostaleSkillsPacketApi(INostaleClient client, Game game)
{
_client = client;
+ _game = game;
}
/// <summary>
@@ 69,6 72,45 @@ public class NostaleSkillsPacketApi
}
/// <summary>
+ /// Use the given (targetable) skill on character itself.
+ /// </summary>
+ /// <remarks>
+ /// For skills that cannot be targeted on an entity, proceed to UseSkillAt.
+ /// </remarks>
+ /// <param name="castId">The cast id of the skill.</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>
+ /// <param name="ct">The cancellation token for cancelling the operation.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public async Task<Result> UseSkillOnCharacter
+ (
+ short castId,
+ short? mapX = default,
+ short? mapY = default,
+ CancellationToken ct = default
+ )
+ {
+ var character = _game.Character;
+ if (character is null)
+ {
+ return new NotInitializedError("Character");
+ }
+
+ return await _client.SendPacketAsync
+ (
+ new UseSkillPacket
+ (
+ castId,
+ EntityType.Player,
+ character.Id,
+ mapX,
+ mapY
+ ),
+ ct
+ );
+ }
+
+ /// <summary>
/// Use the given (targetable) skill on specified entity.
/// </summary>
/// <remarks>
A Core/NosSmooth.Game/Attributes/UnsafeAttribute.cs => Core/NosSmooth.Game/Attributes/UnsafeAttribute.cs +28 -0
@@ 0,0 1,28 @@
+//
+// UnsafeAttribute.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.
+
+namespace NosSmooth.Game.Attributes;
+
+/// <summary>
+/// The given method does not do some checks.
+/// </summary>
+[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Struct)]
+public class UnsafeAttribute : Attribute
+{
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UnsafeAttribute"/> class.
+ /// </summary>
+ /// <param name="reason">The reason.</param>
+ public UnsafeAttribute(string reason)
+ {
+ Reason = reason;
+ }
+
+ /// <summary>
+ /// Gets the unsafe reason.
+ /// </summary>
+ public string Reason { get; }
+}<
\ No newline at end of file
M Core/NosSmooth.Game/Data/Info/Position.cs => Core/NosSmooth.Game/Data/Info/Position.cs +4 -0
@@ 89,4 89,8 @@ public record struct Position(short X, short Y)
{
return new Position((short)(left.X - right.X), (short)(left.Y - right.Y));
}
+
+ /// <inheritdoc />
+ public override string ToString()
+ => $"{X}, {Y}";
}=
\ No newline at end of file
A Core/NosSmooth.Game/Errors/NotInRangeError.cs => Core/NosSmooth.Game/Errors/NotInRangeError.cs +18 -0
@@ 0,0 1,18 @@
+//
+// NotInRangeError.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.Game.Data.Info;
+using Remora.Results;
+
+namespace NosSmooth.Game.Errors;
+
+public record NotInRangeError
+(
+ string Entity,
+ Position EntityPosition,
+ Position TargetPosition,
+ short Range
+) : ResultError($"Entity {Entity} ({EntityPosition}) is not in range ({Range}) of ({TargetPosition}).");<
\ No newline at end of file
A Core/NosSmooth.Game/Errors/NotInitializedError.cs => Core/NosSmooth.Game/Errors/NotInitializedError.cs +15 -0
@@ 0,0 1,15 @@
+//
+// NotInitializedError.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 Remora.Results;
+
+namespace NosSmooth.Game.Errors;
+
+/// <summary>
+/// A game field that had to be accessed was not initialized.
+/// </summary>
+/// <param name="Field">The field.</param>
+public record NotInitializedError(string Field) : ResultError($"The {Field} is not initialized.");<
\ No newline at end of file
M Core/NosSmooth.Game/Extensions/ServiceCollectionExtensions.cs => Core/NosSmooth.Game/Extensions/ServiceCollectionExtensions.cs +4 -0
@@ 65,6 65,10 @@ public static class ServiceCollectionExtensions
.AddPacketResponder<EqResponder>();
serviceCollection
+ .AddTransient<NostaleMapPacketApi>()
+ .AddTransient<NostaleInventoryPacketApi>()
+ .AddTransient<NostaleMatePacketApi>()
+ .AddTransient<NostaleMateSkillsPacketApi>()
.AddTransient<NostaleChatPacketApi>()
.AddTransient<NostaleSkillsPacketApi>();
M Core/NosSmooth.Game/NosSmooth.Game.csproj => Core/NosSmooth.Game/NosSmooth.Game.csproj +0 -4
@@ 16,10 16,6 @@ Move Friends, Group, Family, Inventory to Game</PackageReleaseNotes>
</PropertyGroup>
<ItemGroup>
- <Folder Include="Apis" />
- </ItemGroup>
-
- <ItemGroup>
<ProjectReference Include="..\..\Data\NosSmooth.Data.Abstractions\NosSmooth.Data.Abstractions.csproj" />
<ProjectReference Include="..\NosSmooth.Core\NosSmooth.Core.csproj" />
</ItemGroup>
M Packets/NosSmooth.Packets/Client/Battle/UsePetSkillPacket.cs => Packets/NosSmooth.Packets/Client/Battle/UsePetSkillPacket.cs +6 -6
@@ 16,8 16,8 @@ namespace NosSmooth.Packets.Client.Battle;
/// <param name="TargetEntityType">The target entity type.</param>
/// <param name="TargetId">The target id.</param>
/// <param name="Unknown">Unknown, seems to always be 1.</param>
-/// <param name="Unknown1">Unknown, 6 for Otter.</param>
-/// <param name="Unknown2">Unknown, 9 for Otter.</param>
+/// <param name="MapX">The x coordinate of the pet.</param>
+/// <param name="MapY">The y coordinate of the pet.</param>
[PacketHeader("u_pet", PacketSource.Client)]
[GenerateSerializer(true)]
public record UsePetSkillPacket
@@ 30,8 30,8 @@ public record UsePetSkillPacket
long TargetId,
[PacketIndex(3)]
byte Unknown,
- [PacketIndex(4)]
- byte Unknown1,
- [PacketIndex(5)]
- byte Unknown2
+ [PacketIndex(4, IsOptional = true)]
+ short? MapX,
+ [PacketIndex(5, IsOptional = true)]
+ short? MapY
) : IPacket;=
\ No newline at end of file
A Packets/NosSmooth.Packets/Client/Inventory/GetPacket.cs => Packets/NosSmooth.Packets/Client/Inventory/GetPacket.cs +28 -0
@@ 0,0 1,28 @@
+//
+// GetPacket.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.Packets.Enums.Entities;
+using NosSmooth.PacketSerializer.Abstractions.Attributes;
+
+namespace NosSmooth.Packets.Client.Inventory;
+
+/// <summary>
+/// Pick up an item from ground.
+/// </summary>
+/// <param name="PickerEntityType">The type of the picker entity.</param>
+/// <param name="PickerEntityId">The id of the picker entity.</param>
+/// <param name="GroundItemId">The id of the ground item to pick up.</param>
+[PacketHeader("get", PacketSource.Client)]
+[GenerateSerializer(true)]
+public record GetPacket
+(
+ [PacketIndex(0)]
+ EntityType PickerEntityType,
+ [PacketIndex(1)]
+ long PickerEntityId,
+ [PacketIndex(2)]
+ long GroundItemId
+) : IPacket;<
\ No newline at end of file
A Packets/NosSmooth.Packets/Client/Inventory/MvePacket.cs => Packets/NosSmooth.Packets/Client/Inventory/MvePacket.cs +31 -0
@@ 0,0 1,31 @@
+//
+// MvePacket.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.Packets.Enums.Inventory;
+using NosSmooth.PacketSerializer.Abstractions.Attributes;
+
+namespace NosSmooth.Packets.Client.Inventory;
+
+/// <summary>
+/// Move item from one bag to another (ie. from main to specialists)
+/// </summary>
+/// <param name="SourceBagType">The type of the source bag.</param>
+/// <param name="SourceSlot">The slot in the source bag.</param>
+/// <param name="DestinationBagType">The type of the destination bag.</param>
+/// <param name="DestinationSlot">The destination slot.</param>
+[PacketHeader("mve", PacketSource.Client)]
+[GenerateSerializer(true)]
+public record MvePacket
+(
+ [PacketIndex(0)]
+ BagType SourceBagType,
+ [PacketIndex(1)]
+ short SourceSlot,
+ [PacketIndex(2)]
+ BagType DestinationBagType,
+ [PacketIndex(3)]
+ short DestinationSlot
+) : IPacket;<
\ No newline at end of file
A Packets/NosSmooth.Packets/Client/Inventory/MviPacket.cs => Packets/NosSmooth.Packets/Client/Inventory/MviPacket.cs +31 -0
@@ 0,0 1,31 @@
+//
+// MviPacket.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.Packets.Enums.Inventory;
+using NosSmooth.PacketSerializer.Abstractions.Attributes;
+
+namespace NosSmooth.Packets.Client.Inventory;
+
+/// <summary>
+/// Move item in a bag from one position to another (swap items).
+/// </summary>
+/// <param name="BagType">The type of the bag to move items in.</param>
+/// <param name="SourceSlot">The source slot.</param>
+/// <param name="Amount">The amount to move.</param>
+/// <param name="DestinationSlot">The destination slot.</param>
+[PacketHeader("mvi", PacketSource.Client)]
+[GenerateSerializer(true)]
+public record MviPacket
+(
+ [PacketIndex(0)]
+ BagType BagType,
+ [PacketIndex(1)]
+ short SourceSlot,
+ [PacketIndex(2)]
+ short Amount,
+ [PacketIndex(3)]
+ short DestinationSlot
+) : IPacket;<
\ No newline at end of file
A Packets/NosSmooth.Packets/Client/Inventory/PutPacket.cs => Packets/NosSmooth.Packets/Client/Inventory/PutPacket.cs +25 -0
@@ 0,0 1,25 @@
+//
+// PutPacket.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.Packets.Enums.Inventory;
+using NosSmooth.PacketSerializer.Abstractions.Attributes;
+
+namespace NosSmooth.Packets.Client.Inventory;
+
+/// <summary>
+/// Drop an item from inventory.
+/// </summary>
+[PacketHeader("put", PacketSource.Client)]
+[GenerateSerializer(true)]
+public record PutPacket
+(
+ [PacketIndex(0)]
+ BagType BagType,
+ [PacketIndex(1)]
+ short Slot,
+ [PacketIndex(2)]
+ short Amount
+) : IPacket;<
\ No newline at end of file
A Packets/NosSmooth.Packets/Client/Inventory/UseItemPacket.cs => Packets/NosSmooth.Packets/Client/Inventory/UseItemPacket.cs +25 -0
@@ 0,0 1,25 @@
+//
+// UseItemPacket.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.Packets.Enums.Inventory;
+using NosSmooth.PacketSerializer.Abstractions.Attributes;
+
+namespace NosSmooth.Packets.Client.Inventory;
+
+/// <summary>
+/// Use an item from inventory.
+/// </summary>
+/// <param name="BagType">The type of the bag.</param>
+/// <param name="Slot">The slot in the bag.</param>
+[PacketHeader("u_i", PacketSource.Client)]
+[GenerateSerializer(true)]
+public record UseItemPacket
+(
+ [PacketIndex(0)]
+ BagType BagType,
+ [PacketIndex(1)]
+ short Slot
+) : IPacket;<
\ No newline at end of file
A Packets/NosSmooth.Packets/Client/Movement/PreqPacket.cs => Packets/NosSmooth.Packets/Client/Movement/PreqPacket.cs +16 -0
@@ 0,0 1,16 @@
+//
+// PreqPacket.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.PacketSerializer.Abstractions.Attributes;
+
+namespace NosSmooth.Packets.Client.Movement;
+
+/// <summary>
+/// Walk through portal the character is standing on.
+/// </summary>
+[PacketHeader("preq", PacketSource.Server)]
+[GenerateSerializer(true)]
+public record PreqPacket : IPacket;<
\ No newline at end of file
A Packets/NosSmooth.Packets/Client/NRunPacket.cs => Packets/NosSmooth.Packets/Client/NRunPacket.cs +35 -0
@@ 0,0 1,35 @@
+//
+// NRunPacket.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.Packets.Enums.Entities;
+using NosSmooth.Packets.Enums.NRun;
+using NosSmooth.PacketSerializer.Abstractions.Attributes;
+
+namespace NosSmooth.Packets.Client;
+
+/// <summary>
+/// Npc run packet used for various operations.
+/// </summary>
+/// <param name="Type">The type of the nrun.</param>
+/// <param name="SubType">The subtype, depends on the type. See NosSmooth.Packets.Enums.NRun.</param>
+/// <param name="EntityType">The type of the entity (usually npc).</param>
+/// <param name="EntityId">The id of the entity (npc).</param>
+/// <param name="Confirmation">Unknown function. TODO.</param>
+[PacketHeader("n_run", PacketSource.Client)]
+[GenerateSerializer(true)]
+public record NRunPacket
+(
+ [PacketIndex(0)]
+ NRunType Type,
+ [PacketIndex(1)]
+ short SubType,
+ [PacketIndex(2)]
+ EntityType EntityType,
+ [PacketIndex(3)]
+ long EntityId,
+ [PacketIndex(4, IsOptional = true)]
+ byte? Confirmation
+) : IPacket;<
\ No newline at end of file
A Packets/NosSmooth.Packets/Enums/NRun/MateNRunType.cs => Packets/NosSmooth.Packets/Enums/NRun/MateNRunType.cs +25 -0
@@ 0,0 1,25 @@
+//
+// MateNRunType.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.Diagnostics.CodeAnalysis;
+
+#pragma warning disable CS1591
+namespace NosSmooth.Packets.Enums.NRun;
+
+/// <summary>
+/// A subtype for <see cref="NRunType"/>.Mate.
+/// </summary>
+[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:Enumeration items should be documented", Justification = "Unknown yet.")]
+public enum MateNRunType
+{
+ Company = 2,
+ Stay = 3,
+ RequestPetSendBack = 4,
+ TriggerPetSendBack = 5,
+ KickPet = 6,
+ TriggerSummon = 7,
+ Summon = 9
+}<
\ No newline at end of file
A Packets/NosSmooth.Packets/Enums/NRun/NRunType.cs => Packets/NosSmooth.Packets/Enums/NRun/NRunType.cs +214 -0
@@ 0,0 1,214 @@
+//
+// NRunType.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.Diagnostics.CodeAnalysis;
+using NosSmooth.Packets.Client;
+
+#pragma warning disable CS1591
+namespace NosSmooth.Packets.Enums.NRun;
+
+/// <summary>
+/// A type of <see cref="NRunPacket"/> packet.
+/// </summary>
+[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:Enumeration items should be documented", Justification = "Too many fields.")]
+public enum NRunType
+{
+ TimesUp = 0,
+ ChangeClass = 1,
+ UpgradeItemNpc = 2,
+ GetPartner = 3,
+ Mate = 4,
+ TimeSpaceTimer = 5,
+ TimeSpaceEndDialog = 6,
+ JewelryCellon = 10,
+ OpenWindow = 12,
+ OpenCrafting = 14,
+ SetRevivalSpawn = 15,
+ WarpTeleport = 16,
+ AskEnterArena = 17,
+ CircleTimeSkill = 18,
+ TimeSpaceUnknown = 19,
+ FamilyDialogue = 23,
+ WarpTeleportAct5 = 26,
+ GrasslinEvent = 31,
+ Nos11YearMedalProduction = 36,
+ NosProduction = 37,
+ Year11Production = 38,
+ RaidList = 39,
+ EggFairyProduction = 40,
+ GrasslinSealProduction = 41,
+ WeddingGazebo = 45,
+ RedMagicFairyProduction = 46,
+ OpenNosbazaar = 60,
+ GrenigasSealProduction = 61,
+ TeleportGrenigasSquare = 62,
+ IcyFlowersMission = 65,
+ HeatPotionMission = 66,
+ JohnMission = 67,
+ AkamurMission = 68,
+ IceFlowers10Production = 69,
+ MagicCamelProduction = 70,
+ MagicCamelBoxProduction = 71,
+ TimespaceOnFinishDialog = 144,
+ LandOfChaosEnter = 150,
+ UseBank = 321,
+ GetBankBook = 322,
+ AmoraMission = 327,
+ ShowPlayerShop = 900,
+ EnterToTsId = 1000,
+ DailyMissionTarot = 1500,
+
+ /* HALLOWEEN */
+ HalloweenProductionBox = 47,
+ HalloweenProductionCostume10YellowCandy = 72,
+ HalloweenProductionCostume10BlackCandy = 73,
+ HalloweenProductionSeal30YellowCandy = 74,
+ HalloweenProductionSeal30BlackCandy = 75,
+ HalloweenProductionSealBagOfSweats = 76,
+ HalloweenMissionMary = 77,
+ HalloweenMissionEva = 78,
+ HalloweenMissionMalcolm = 79,
+ HalloweenMissionTeoman = 80,
+ HalloweenMissionEric = 81,
+ HalloweenCatacombsEntrance = 316,
+ HalloweenProductionLaurenaWand = 317,
+
+ /* WINTER */
+ WinterProductionBox = 34,
+ WinterProductionSnowmanSeal = 35,
+ WinterMissionSanta = 82,
+ WinterProductionSpecialBag = 83,
+ WinterProductionSealedChristVessel30CreamCake = 84,
+ WinterProductionCostume30ChocolateCake = 85,
+ WinterProductionSeal30CreamCake = 86,
+ WinterProductionSeal30ChocolateCake = 87,
+ WinterProduction5StockingsChristBox = 88,
+ WinterMissionSlugg = 89,
+ WinterMissionEva = 90,
+ WinterMissionMalcolm = 91,
+ WinterMissionTeoman = 92,
+ WinterMissionSoraya = 93,
+ WinterMissionVikings = 128,
+ WinterProduction10Happy = 129,
+ WinterProduction10HappyYear = 130,
+ WinterMissionFirstSonPark1 = 178,
+ WinterMissionFirstSonPark2 = 179,
+ WinterMissionSecondSonPark1 = 180,
+ WinterMissionSecondSonPark2 = 181,
+ WinterMissionThirdSonPark1 = 182,
+ WinterMissionThirdSonPark2 = 183,
+ WinterMissionWanderer1 = 184,
+ WinterMissionWanderer2 = 185,
+ WinterMissionWanderer3 = 186,
+ WinterMissionMaru1 = 187,
+ WinterMissionMaru2 = 188,
+ WinterProduction3BrassCoinsBox = 190,
+ WinterProductionBetterSleigh = 325,
+ WinterMissionReindeer = 326,
+ WinterProductionHappy = 6013,
+ WinterProductionHappyYear = 6014,
+
+ /* EASTER */
+ EasterMissionMimi = 94,
+ EasterProduction5GoldEggsBox = 95,
+ EasterProduction30ChocolateQfcSeal = 96,
+ EasterMissionSlugg = 97,
+ EasterMissionCalvin = 98,
+ EasterMissionEva = 99,
+ EasterMissionMalcolm = 100,
+ EasterMissionHungbu = 191,
+ EasterMissionHungbuSwallow = 192,
+ EasterProduction10CleanEggsCake = 328,
+ EasterProduction10CleanEggsBox = 329,
+ EasterProduction10RotterEggsClean = 330,
+ EasterMissionSluggStory = 331,
+
+ /* SP5 & SP6 MISSIONS/PRODUCTION */
+ DracoAmuletMission = 110,
+ GlacerusAmuletMission = 131,
+ DracoSealProduction = 111,
+ EnterToWatterGroto = 132,
+ GlacerusSealProduction = 133,
+ GiveSp6TsMission = 134,
+ DracoClawSp5Production = 145,
+ DracoClawSp5PerfStonProduction = 146,
+ GlacerusManeSp6Production = 147,
+ GlacerusManeSp6PerfStoneProduction = 148,
+
+ /* ARENA OF TALENTS/MASTERS */
+ ArenaOfTalentsRegistration = 135,
+ ArenaOfMastersRegistration = 136,
+ ArenaOfMastersSpectatorChoice = 137,
+ ArenaOfTalentsSpectatorRandom = 138,
+ ArenaOfMastersSpectatorRandom = 139,
+ ArenaOfMastersRanking = 140,
+
+ /* SP8 MISSIONS/PRODUCTION */
+ Sp8MissionLaurena = 193,
+ Sp8ProductionSoulSliver3Powder = 194,
+ Sp8Production5SeedDamnationSeal = 195,
+
+ /* SUMMER */
+ SummerMissionRaphaelStory = 197,
+ SummerProductionBunnyLiverWithBox = 198,
+ SummerProductionBunnyLiver = 199,
+ SummerMissionRaphael = 201,
+ SummerMissionJack = 1507,
+ SummerEventPirateSp = 1508,
+ SummerProduction10Mariner10MapSeal = 1509,
+ SummerProductionPirateSpLuck = 1511,
+
+ /* ACT 6 */
+ Act61MissionFirst = 300,
+ Act61TeleportCylloan = 301,
+ Act62MissionFirst = 302,
+ Act62MissionHealingChoiceSave = 303,
+ Act62MissionHealingChoiceNoSave = 304,
+ Act62EnterToShip = 305,
+ Act62EnterToShip2 = 306,
+ Act62BackToCylloan = 307,
+
+ /* VALHALLA */
+ ValhallaMissionBarni = 308,
+ ValhallaMissionRagnar10KkChoice = 309,
+ ValhallaMissionRagnarFreeChoice = 310,
+ ValhallaMissionRagnar = 311,
+ ValhallaMissionFrigg = 312,
+ ValhallaEnterToTartHap = 313,
+ ValhallaBackToPort = 314,
+ ValhallaMissionJennifer = 315,
+
+ /* MARTIAL ARTIST */
+ MaMissionSp2 = 324,
+ MaMissionSp3 = 340,
+
+ /* FAMILY */
+ FamilyWarehouseOpen = 1600,
+ FamilyWarehouseHistory = 1601,
+ FamilyUpgradeWarehouse21 = 1602,
+ FamilyUpgradeWarehouse49 = 1603,
+ FamilyUpgradeMembersSize70 = 1604,
+ FamilyUpgradeMembersSize100 = 1605,
+
+ /* QUESTS */
+ QuestAdditionalAct5 = 200,
+ QuestAdditional = 2000,
+ QuestEndSp = 2001,
+ QuestTeleportToSpMap = 2002,
+ QuestReceive = 3000,
+ QuestReceiveMain = 3001,
+ QuestReceiveTsPoints = 3002,
+ QuestReceiveAdditionalBlue = 3006, // (\!/)
+
+ /* ACT 4/5 */
+ Act4EnterShip = 5001,
+ Act4Leave = 5002,
+ Act4LeaveShip = 5004,
+ Act4Enter = 5005,
+ Act5EnterShip = 5011,
+ Act5Leave = 5012,
+ Act5LeaveShip = 5014
+}<
\ No newline at end of file