~ruther/NosSmooth

3d131cb63fb7f4ea5029300e8487384e94440cc8 — Rutherther 2 years ago cc1c243 + 18e9118
Merge pull request #51 from Rutherther/feat/easy-api

Add easy apis for various things
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

Do not follow this link