~ruther/NosSmooth

e8b3d21e9f32c6c206635e1608be4b1ca0edd0fd — Rutherther 3 years ago ea69974
feat(game): add basic entity and map data
M Core/NosSmooth.Game/Data/Characters/Character.cs => Core/NosSmooth.Game/Data/Characters/Character.cs +63 -86
@@ 16,89 16,66 @@ namespace NosSmooth.Game.Data.Characters;
/// <summary>
/// Represents the client character.
/// </summary>
/// <param name="Inventory">The character's inventory with items.</param>
/// <param name="Family">The family of the character, if any..</param>
/// <param name="Friends">The friends of the character.</param>
/// <param name="Skills">The current skill set of the character.</param>
/// <param name="Group">The group the character is in, if any. Contains pets and partners as well.</param>
/// <param name="SkillCp">The skill cp amount used for learning new skills.</param>
/// <param name="Id">The id of the character entity.</param>
/// <param name="Name">The name of the character entity.</param>
/// <param name="Position">The position of the character.</param>
/// <param name="Speed">The movement speed of the character.</param>
/// <param name="Level">The </param>
/// <param name="JobLevel">The </param>
/// <param name="HeroLevel">The </param>
/// <param name="Direction"></param>
/// <param name="Hp"></param>
/// <param name="Mp"></param>
/// <param name="Faction"></param>
/// <param name="Size"></param>
/// <param name="AuthorityType"></param>
/// <param name="Sex"></param>
/// <param name="HairStyle"></param>
/// <param name="HairColor"></param>
/// <param name="Class"></param>
/// <param name="Icon"></param>
/// <param name="Compliment"></param>
/// <param name="Morph"></param>
/// <param name="ArenaWinner"></param>
/// <param name="Invisible"></param>
/// <param name="Reputation"></param>
public record Character
(
    Inventory.Inventory? Inventory = default,
    Family? Family = default,
    IReadOnlyList<Friend>? Friends = default,
    Skills? Skills = default,
    Group? Group = default,
    int? SkillCp = default,
    long Id = default,
    string? Name = default,
    Position? Position = default,
    byte? Speed = default,
    Level? Level = default,
    Level? JobLevel = default,
    Level? HeroLevel = default,
    byte? Direction = default,
    Health? Hp = default,
    Health? Mp = default,
    FactionType? Faction = default,
    short Size = default,
    AuthorityType AuthorityType = default,
    SexType Sex = default,
    HairStyle HairStyle = default,
    HairColor HairColor = default,
    PlayerClass Class = default,
    byte? Icon = default,
    short? Compliment = default,
    Morph? Morph = default,
    bool? ArenaWinner = default,
    bool? Invisible = default,
    long? Reputation = default,
    IReadOnlyList<long>? EffectsVNums = default
) : Player(
    Id,
    Name,
    Position,
    Speed,
    Level,
    HeroLevel,
    Direction,
    Hp,
    Mp,
    Faction,
    Size,
    AuthorityType,
    Sex,
    HairStyle,
    HairColor,
    Class,
    Icon,
    Compliment,
    Morph,
    ArenaWinner,
    Invisible,
    Reputation,
    EffectsVNums
);
\ No newline at end of file
public class Character : Player
{
    /// <summary>
    /// Gets or sets whether the character can't move.
    /// </summary>
    public bool Stunned { get; set; }

    /// <summary>
    /// Gets or sets the inventory of the character.
    /// </summary>
    public Inventory.Inventory? Inventory { get; set; }

    /// <summary>
    /// Get or sets the friends of the character.
    /// </summary>
    public IReadOnlyList<Friend>? Friends { get; set; }

    /// <summary>
    /// Gets or sets the skills of the player.
    /// </summary>
    public Skills? Skills { get; set; }

    /// <summary>
    /// Gets or sets the group the player is in.
    /// </summary>
    public Group? Group { get; set; }

    /// <summary>
    /// Gets or sets the c skill points.
    /// </summary>
    public int? SkillCp { get; set; }

    /// <summary>
    /// Gets or sets the job level.
    /// </summary>
    public Level? JobLevel { get; set; }

    /// <summary>
    /// Gets or sets the player level.
    /// </summary>
    public Level? PlayerLevel { get; set; }

    /// <summary>
    /// Gets or sets the player level.
    /// </summary>
    public Level? HeroLevelStruct { get; set; }

    /// <inheritdoc/>
    public override short? HeroLevel
    {
        get => HeroLevelStruct?.Lvl;
        set
        {
            if (HeroLevelStruct is not null && value is not null)
            {
                HeroLevelStruct = HeroLevelStruct with
                {
                    Lvl = value.Value
                };
            }
        }
    }
}
\ No newline at end of file

M Core/NosSmooth.Game/Data/Characters/Skill.cs => Core/NosSmooth.Game/Data/Characters/Skill.cs +3 -6
@@ 4,6 4,8 @@
//  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.Data.Abstractions.Infos;

namespace NosSmooth.Game.Data.Characters;

/// <summary>


@@ 11,7 13,7 @@ namespace NosSmooth.Game.Data.Characters;
/// </summary>
/// <param name="SkillVNum">The vnum of the skill.</param>
/// <param name="Level">The level of the skill. Unknown feature.</param>
public record Skill(long SkillVNum, int? Level = default)
public record Skill(int SkillVNum, int? Level = default, ISkillInfo? Info = default)
{
    /// <summary>
    /// Gets the last time this skill was used.


@@ 19,11 21,6 @@ public record Skill(long SkillVNum, int? Level = default)
    public DateTimeOffset LastUseTime { get; internal set; }

    /// <summary>
    /// Gets the cooldown of the skill.
    /// </summary>
    public TimeSpan? Cooldown { get; internal set; }

    /// <summary>
    /// Gets whether the skill is on cooldown.
    /// </summary>
    /// <remarks>

M Core/NosSmooth.Game/Data/Entities/GroundItem.cs => Core/NosSmooth.Game/Data/Entities/GroundItem.cs +37 -8
@@ 4,6 4,7 @@
//  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.Data.Abstractions.Infos;
using NosSmooth.Game.Data.Info;
using NosSmooth.Packets.Enums;



@@ 12,14 13,42 @@ namespace NosSmooth.Game.Data.Entities;
/// <summary>
/// The item on the ground.
/// </summary>
/// <param name="Id">The id of the item entity.</param>
/// <param name="ItemVNum">The vnum of the dropped item.</param>
/// <param name="Position">The position of the ground item.</param>
public record GroundItem(long Id, long ItemVNum, Position? Position) : IEntity
public class GroundItem : IEntity
{
    /// <inheritdoc />
    public string? Name => null;
    /// <summary>
    /// Gets or sets the id of the owner, if any.
    /// </summary>
    public long? OwnerId { get; set; }

    /// <inheritdoc />
    public EntityType Type => EntityType.Object;
    /// <summary>
    /// Gets or sets the amount of the item on the ground.
    /// </summary>
    public int Amount { get; internal set; }

    /// <summary>
    /// Gets or sets whether the item is for a quest.
    /// </summary>
    public bool IsQuestRelated { get; internal set; }

    /// <summary>
    /// Gets or sets the info about the item, if available.
    /// </summary>
    public IItemInfo? ItemInfo { get; internal set; }

    /// <summary>
    /// Gets the VNum of the npc.
    /// </summary>
    public int VNum { get; internal set; }

    /// <inheritdoc/>
    public long Id { get; set; }

    /// <inheritdoc/>
    public Position? Position { get; set; }

    /// <inheritdoc/>
    public EntityType Type
    {
        get => EntityType.Object;
    }
}
\ No newline at end of file

M Core/NosSmooth.Game/Data/Entities/IEntity.cs => Core/NosSmooth.Game/Data/Entities/IEntity.cs +2 -7
@@ 17,17 17,12 @@ public interface IEntity
    /// <summary>
    /// Gets the id of the entity.
    /// </summary>
    public long Id { get; }

    /// <summary>
    /// Gets the name of the entity. May be null if unknown.
    /// </summary>
    public string? Name { get; }
    public long Id { get; set; }

    /// <summary>
    /// Gets the position of the entity.
    /// </summary>
    public Position? Position { get; }
    public Position? Position { get; set; }

    /// <summary>
    /// Gets the type of the entity.

M Core/NosSmooth.Game/Data/Entities/LivingEntity.cs => Core/NosSmooth.Game/Data/Entities/LivingEntity.cs +33 -8
@@ 17,40 17,65 @@ public interface ILivingEntity : IEntity
    /// <summary>
    /// Gets the speed of the entity. May be null if unknown.
    /// </summary>
    public byte? Speed { get; }
    public int? Speed { get; set; }

    /// <summary>
    /// Gets or sets whether the player is invisible.
    /// </summary>
    public bool? IsInvisible { get; set; }

    /// <summary>
    /// Gets the level of the entity. May be null if unknown.
    /// </summary>
    public ushort? Level { get; }
    public ushort? Level { get; set; }

    /// <summary>
    /// Gets the direction the entity is looking. May be null if unknown.
    /// </summary>
    public byte? Direction { get; }
    public byte? Direction { get; set; }

    /// <summary>
    /// Gets the percentage of the health points of the entity. May be null if unknown.
    /// </summary>
    public Health? Hp { get; }
    public Health? Hp { get; set; }

    /// <summary>
    /// Gets the percentage of the mana points of the entity. May be null if unknown.
    /// </summary>
    public Health? Mp { get; }
    public Health? Mp { get; set; }

    /// <summary>
    /// Gets the faction of the entity. May be null if unknown.
    /// </summary>
    public FactionType? Faction { get; }
    public FactionType? Faction { get; set; }

    /// <summary>
    /// Gets the size of the entity.
    /// </summary>
    public short Size { get; }
    public short Size { get; set; }

    /// <summary>
    /// Gets the VNums of the effects the entity has.
    /// </summary>
    public IReadOnlyList<long>? EffectsVNums { get; }
    public IReadOnlyList<long>? EffectsVNums { get; set; }

    /// <summary>
    /// Gets the name of the entity. May be null if unknown.
    /// </summary>
    public string? Name { get; set; }

    /// <summary>
    /// Gets or sets whether the entity is sitting.
    /// </summary>
    public bool IsSitting { get; set; }

    /// <summary>
    /// Gets or sets whether the entity cannot move.
    /// </summary>
    public bool CantMove { get; set; }

    /// <summary>
    /// Gets or sets whether the entity cannot attack.
    /// </summary>
    public bool CantAttack { get; set; }
}
\ No newline at end of file

M Core/NosSmooth.Game/Data/Entities/Monster.cs => Core/NosSmooth.Game/Data/Entities/Monster.cs +57 -26
@@ 4,6 4,7 @@
//  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.Data.Abstractions.Infos;
using NosSmooth.Game.Data.Info;
using NosSmooth.Packets.Enums;



@@ 12,33 13,63 @@ namespace NosSmooth.Game.Data.Entities;
/// <summary>
/// Represents nostale monster entity.
/// </summary>
/// <param name="Id"></param>
/// <param name="Name"></param>
/// <param name="Position"></param>
/// <param name="Speed"></param>
/// <param name="Level"></param>
/// <param name="Direction"></param>
/// <param name="Hp"></param>
/// <param name="Mp"></param>
/// <param name="Faction"></param>
/// <param name="Size"></param>
/// <param name="MonsterVNum"></param>
public record Monster
(
    long Id,
    string? Name,
    Position? Position,
    byte? Speed,
    ushort? Level,
    byte? Direction,
    Health? Hp,
    Health? Mp,
    FactionType? Faction,
    short Size,
    long MonsterVNum,
    IReadOnlyList<long>? EffectsVNums = default
) : ILivingEntity
public class Monster : ILivingEntity
{
    /// <summary>
    /// Gets or sets the monster info.
    /// </summary>
    public IMonsterInfo? MonsterInfo { get; set; }

    /// <summary>
    /// Gets the VNum of the monster.
    /// </summary>
    public int VNum { get; set; }

    /// <inheritdoc/>
    public long Id { get; set; }

    /// <inheritdoc/>
    public string? Name { get; set; }

    /// <inheritdoc />
    public bool IsSitting { get; set; }

    /// <inheritdoc />
    public bool CantMove { get; set; }

    /// <inheritdoc />
    public bool CantAttack { get; set; }

    /// <inheritdoc/>
    public Position? Position { get; set; }

    /// <inheritdoc/>
    public EntityType Type => EntityType.Monster;

    /// <inheritdoc/>
    public int? Speed { get; set; }

    /// <inheritdoc />
    public bool? IsInvisible { get; set; }

    /// <inheritdoc/>
    public ushort? Level { get; set; }

    /// <inheritdoc/>
    public byte? Direction { get; set; }

    /// <inheritdoc/>
    public Health? Hp { get; set; }

    /// <inheritdoc/>
    public Health? Mp { get; set; }

    /// <inheritdoc/>
    public FactionType? Faction { get; set; }

    /// <inheritdoc/>
    public short Size { get; set; }

    /// <inheritdoc/>
    public IReadOnlyList<long>? EffectsVNums { get; set; }
}
\ No newline at end of file

M Core/NosSmooth.Game/Data/Entities/Npc.cs => Core/NosSmooth.Game/Data/Entities/Npc.cs +58 -1
@@ 4,9 4,66 @@
//  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 NosSmooth.Packets.Enums;

namespace NosSmooth.Game.Data.Entities;

/// <summary>
/// Represents nostale npc entity.
/// </summary>
public record Npc();
\ No newline at end of file
public class Npc : ILivingEntity
{
    /// <summary>
    /// Gets the VNum of the npc.
    /// </summary>
    public int VNum { get; internal set; }

    /// <inheritdoc/>
    public long Id { get; set; }

    /// <inheritdoc/>
    public string? Name { get; set; }

    /// <inheritdoc />
    public bool IsSitting { get; set; }

    /// <inheritdoc />
    public bool CantMove { get; set; }

    /// <inheritdoc />
    public bool CantAttack { get; set; }

    /// <inheritdoc/>
    public Position? Position { get; set; }

    /// <inheritdoc/>
    public EntityType Type => EntityType.Npc;

    /// <inheritdoc/>
    public int? Speed { get; set; }

    /// <inheritdoc />
    public bool? IsInvisible { get; set; }

    /// <inheritdoc/>
    public ushort? Level { get; set; }

    /// <inheritdoc/>
    public byte? Direction { get; set; }

    /// <inheritdoc/>
    public Health? Hp { get; set; }

    /// <inheritdoc/>
    public Health? Mp { get; set; }

    /// <inheritdoc/>
    public FactionType? Faction { get; set; }

    /// <inheritdoc/>
    public short Size { get; set; }

    /// <inheritdoc/>
    public IReadOnlyList<long>? EffectsVNums { get; set; }
}
\ No newline at end of file

M Core/NosSmooth.Game/Data/Entities/Player.cs => Core/NosSmooth.Game/Data/Entities/Player.cs +121 -47
@@ 5,6 5,8 @@
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using NosSmooth.Game.Data.Info;
using NosSmooth.Game.Data.Items;
using NosSmooth.Game.Data.Social;
using NosSmooth.Packets.Enums;
using NosSmooth.Packets.Enums.Players;



@@ 13,56 15,128 @@ namespace NosSmooth.Game.Data.Entities;
/// <summary>
/// Represents nostale player entity.
/// </summary>
/// <param name="Id">The id of the player.</param>
/// <param name="Name">The name of the player.</param>
/// <param name="Position">The position the player is at.</param>
/// <param name="Speed"></param>
/// <param name="Level"></param>
/// <param name="Direction"></param>
/// <param name="Hp"></param>
/// <param name="Mp"></param>
/// <param name="Faction"></param>
/// <param name="Size"></param>
/// <param name="AuthorityType"></param>
/// <param name="Sex"></param>
/// <param name="HairStyle"></param>
/// <param name="HairColor"></param>
/// <param name="Class"></param>
/// <param name="Icon"></param>
/// <param name="Compliment"></param>
/// <param name="Morph"></param>
/// <param name="ArenaWinner"></param>
/// <param name="Invisible"></param>
public record Player
(
    long Id,
    string? Name = default,
    Position? Position = default,
    byte? Speed = default,
    Level? Level = default,
    Level? HeroLevel = default,
    byte? Direction = default,
    Health? Hp = default,
    Health? Mp = default,
    FactionType? Faction = default,
    short Size = default,
    AuthorityType AuthorityType = default,
    SexType Sex = default,
    HairStyle HairStyle = default,
    HairColor HairColor = default,
    PlayerClass Class = default,
    byte? Icon = default,
    short? Compliment = default,
    Morph? Morph = default,
    bool? ArenaWinner = default,
    bool? Invisible = default,
    long? Reputation = default,
    IReadOnlyList<long>? EffectsVNums = default
) : ILivingEntity
public class Player : ILivingEntity
{
    /// <summary>
    /// Gets or sets the authority of the player.
    /// </summary>
    public AuthorityType Authority { get; set; }

    /// <summary>
    /// Gets or sets the sex of the player.
    /// </summary>
    public SexType Sex { get; set; }

    /// <summary>
    /// Gets or sets the hairstyle of the player.
    /// </summary>
    public HairStyle HairStyle { get; set; }

    /// <summary>
    /// Gets or sets the hair color of the player.
    /// </summary>
    public HairColor HairColor { get; set; }

    /// <summary>
    /// Gets or sets the class of the player.
    /// </summary>
    public PlayerClass Class { get; set; }

    /// <summary>
    /// Gets or sets the reputation icon. UNKNOWN TODO.
    /// </summary>
    public byte? Icon { get; set; }

    /// <summary>
    /// UNKNOWN TODO.
    /// </summary>
    public short? Compliment { get; set; }

    /// <summary>
    /// Gets or sets the morph used for sps, vehicles and such.
    /// </summary>
    public Morph? Morph { get; set; }

    /// <summary>
    /// Gets or sets whether the player is a champion arena winner.
    /// </summary>
    public bool ArenaWinner { get; set; }

    /// <summary>
    /// Gets or sets the reputation number of the player.
    /// </summary>
    public long? Reputation { get; set; }

    /// <summary>
    /// Gets or sets the visible title of the player.
    /// </summary>
    public short Title { get; set; }

    /// <summary>
    /// Gets or sets the family.
    /// </summary>
    public Family? Family { get; set; }

    /// <summary>
    /// Gets the VNum of the npc.
    /// </summary>
    public int VNum { get; set; }

    /// <inheritdoc/>
    public long Id { get; set; }

    /// <inheritdoc/>
    ushort? ILivingEntity.Level => Level?.Lvl;
    public string? Name { get; set; }

    /// <inheritdoc />
    public bool IsSitting { get; set; }

    /// <inheritdoc />
    public bool CantMove { get; set; }

    /// <inheritdoc />
    public bool CantAttack { get; set; }

    /// <inheritdoc/>
    public Position? Position { get; set; }

    /// <inheritdoc/>
    public EntityType Type => EntityType.Player;

    /// <inheritdoc/>
    public int? Speed { get; set; }

    /// <inheritdoc />
    public bool? IsInvisible { get; set; }

    /// <inheritdoc/>
    public ushort? Level { get; set; }

    /// <inheritdoc/>
    public byte? Direction { get; set; }

    /// <inheritdoc/>
    public Health? Hp { get; set; }

    /// <inheritdoc/>
    public Health? Mp { get; set; }

    /// <inheritdoc/>
    public FactionType? Faction { get; set; }

    /// <inheritdoc/>
    public short Size { get; set; }

    /// <inheritdoc/>
    public IReadOnlyList<long>? EffectsVNums { get; set; }

    /// <summary>
    /// Gets or sets the hero level.
    /// </summary>
    public virtual short? HeroLevel { get; set; }

    /// <summary>
    /// Gets or sets the equipment.
    /// </summary>
    public Equipment? Equipment { get; set; }
}
\ No newline at end of file

M Core/NosSmooth.Game/Data/Info/Health.cs => Core/NosSmooth.Game/Data/Info/Health.cs +99 -4
@@ 9,9 9,104 @@ namespace NosSmooth.Game.Data.Info;
/// <summary>
/// Represents the health or mana of an entity.
/// </summary>
/// <param name="Amount">The current amount of health.</param>
/// <param name="Maximum">The maximum amount of health.</param>
public record Health(long Amount, long Maximum)
public class Health
{
    private decimal Percentage => (decimal)Amount / Maximum;
    private byte? _percentage;
    private long? _amount;
    private long? _maximum;

    /// <summary>
    /// Gets or sets the percentage of the health.
    /// </summary>
    public byte? Percentage
    {
        get => _percentage;
        set
        {
            _percentage = value;
            if (value is null)
            {
                return;
            }

            var maximum = _maximum;
            if (maximum is not null)
            {
                _amount = (long)((value / 100.0) * maximum);
                return;
            }

            var amount = _amount;
            if (amount is not null)
            {
                _maximum = (long)(amount / (value / 100.0));
            }
        }
    }

    /// <summary>
    /// Gets or sets the health amount.
    /// </summary>
    public long? Amount
    {
        get => _amount;
        set
        {
            _amount = value;
            if (value is null)
            {
                return;
            }

            var maximum = _maximum;
            if (maximum is not null)
            {
                _percentage = (byte)(((double)value / maximum) * 100);
                return;
            }

            var percentage = _percentage;
            if (percentage is not null)
            {
                _maximum = (long)(value / (percentage / 100.0));
            }
        }
    }

    /// <summary>
    /// Gets or sets the maximum health.
    /// </summary>
    public long? Maximum
    {
        get => _maximum;
        set
        {
            _maximum = value;
            if (value is null)
            {
                return;
            }

            var amount = _amount;
            var percentage = _percentage;

            if (amount is not null)
            {
                if (amount > value)
                {
                    amount = _amount = value;
                    _percentage = 100;
                    return;
                }

                _percentage = (byte)((amount / (double)value) * 100);
                return;
            }

            if (percentage is not null)
            { // ? would this be correct?
                _amount = (long)((percentage / 100.0) * value);
            }
        }
    }
}
\ No newline at end of file

M Core/NosSmooth.Game/Data/Info/Level.cs => Core/NosSmooth.Game/Data/Info/Level.cs +1 -1
@@ 12,4 12,4 @@ namespace NosSmooth.Game.Data.Info;
/// <param name="Lvl">The level.</param>
/// <param name="Xp">Current xp.</param>
/// <param name="XpLoad">Maximum xp of the current level.</param>
public record Level(ushort Lvl, long Xp, long XpLoad);
\ No newline at end of file
public record Level(short Lvl, long Xp, long XpLoad);
\ No newline at end of file

M Core/NosSmooth.Game/Data/Info/Position.cs => Core/NosSmooth.Game/Data/Info/Position.cs +11 -8
@@ 4,20 4,23 @@
//  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;

namespace NosSmooth.Game.Data.Info;

/// <summary>
/// Represents nostale position on map.
/// </summary>
public record Position
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1313", MessageId = "Parameter names should begin with lower-case letter", Justification = "Standard.")]
public record struct Position(long X, long Y)
{
    /// <summary>
    /// Gets the x coordinate.
    /// </summary>
    public long X { get; internal set; }

    /// <summary>
    /// Gets the y coordinate.
    /// Get the squared distance to the given position.
    /// </summary>
    public long Y { get; internal set; }
    /// <param name="position">The position.</param>
    /// <returns>The distance squared.</returns>
    public long DistanceSquared(Position position)
    {
        return ((position.X - X) * (position.X - X)) + ((position.Y - Y) * (position.Y - Y));
    }
}
\ No newline at end of file

A Core/NosSmooth.Game/Data/Items/Equipment.cs => Core/NosSmooth.Game/Data/Items/Equipment.cs +21 -0
@@ 0,0 1,21 @@
//
//  Equipment.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.Data.Items;

public record Equipment
(
     Item? Hat,
     UpgradeableItem? Armor,
     UpgradeableItem? MainWeapon,
     UpgradeableItem? SecondaryWeapon,
     Item? Mask,
     Item? Fairy,
     Item? CostumeSuit,
     Item? CostumeHat,
     short? WeaponSkin,
     short? WingSkin
);
\ No newline at end of file

A Core/NosSmooth.Game/Data/Items/Fairy.cs => Core/NosSmooth.Game/Data/Items/Fairy.cs +12 -0
@@ 0,0 1,12 @@
//
//  Fairy.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.Data.Abstractions.Infos;
using NosSmooth.Packets.Enums;

namespace NosSmooth.Game.Data.Items;

public record Fairy(int ItemVNum, Element Element, IItemInfo? Info) : Item(ItemVNum, Info);
\ No newline at end of file

A Core/NosSmooth.Game/Data/Items/Item.cs => Core/NosSmooth.Game/Data/Items/Item.cs +16 -0
@@ 0,0 1,16 @@
//
//  Item.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.Data.Abstractions.Infos;

namespace NosSmooth.Game.Data.Items;

/// <summary>
/// A NosTale item.
/// </summary>
/// <param name="ItemVNum"></param>
/// <param name="Info"></param>
public record Item(int ItemVNum, IItemInfo? Info);
\ No newline at end of file

A Core/NosSmooth.Game/Data/Items/UpgradeableItem.cs => Core/NosSmooth.Game/Data/Items/UpgradeableItem.cs +18 -0
@@ 0,0 1,18 @@
//
//  UpgradeableItem.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.Data.Abstractions.Infos;

namespace NosSmooth.Game.Data.Items;

/// <summary>
/// An item that can be upgraded and has rarity, ie. weapon or armor.
/// </summary>
/// <param name="ItemVNum">The vnum of the item.</param>
/// <param name="Info">The information about the item.</param>
/// <param name="Upgrade">The upgrade (0 - 10).</param>
/// <param name="Rare">The rare nubmer (0 - 8).</param>
public record UpgradeableItem(int ItemVNum, IItemInfo? Info, byte? Upgrade, sbyte? Rare) : Item(ItemVNum, Info);
\ No newline at end of file

M Core/NosSmooth.Game/Data/Maps/Map.cs => Core/NosSmooth.Game/Data/Maps/Map.cs +35 -1
@@ 4,9 4,43 @@
//  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.Data.Abstractions.Infos;
using NosSmooth.Game.Data.Info;

namespace NosSmooth.Game.Data.Maps;

/// <summary>
/// Represents nostale map.
/// </summary>
public record Map();
\ No newline at end of file
public record Map
(
    long Id,
    byte Type,
    IMapInfo? Info,
    MapEntities Entities,
    IReadOnlyList<Portal> Portals
)
{
    /// <summary>
    /// Gets whether the given position lies on a portal.
    /// </summary>
    /// <param name="position">The position.</param>
    /// <param name="portal">The portal the position is on, if any.</param>
    /// <returns>Whether there was a portal at the specified position.</returns>
    public bool IsOnPortal(Position position, [NotNullWhen(true)] out Portal? portal)
    {
        foreach (var p in Portals)
        {
            // TODO: figure out the distance
            if (p.Position.DistanceSquared(position) < 3)
            {
                portal = p;
                return true;
            }
        }

        portal = null;
        return false;
    }
}
\ No newline at end of file

A Core/NosSmooth.Game/Data/Maps/MapEntities.cs => Core/NosSmooth.Game/Data/Maps/MapEntities.cs +100 -0
@@ 0,0 1,100 @@
//
//  MapEntities.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.Concurrent;
using NosSmooth.Game.Data.Entities;
using NosSmooth.Packets.Enums;

namespace NosSmooth.Game.Data.Maps;

/// <summary>
/// Thread-safe store for the entities on the map.
/// </summary>
public class MapEntities
{
    private readonly ConcurrentDictionary<long, IEntity> _entities;

    /// <summary>
    /// Initializes a new instance of the <see cref="MapEntities"/> class.
    /// </summary>
    public MapEntities()
    {
        _entities = new ConcurrentDictionary<long, IEntity>();
    }

    /// <summary>
    /// Gets the given entity by id.
    /// </summary>
    /// <param name="id">The id of the entity.</param>
    /// <returns>The entity, or null, if not found.</returns>
    public IEntity? GetEntity(long id)
        => _entities.GetValueOrDefault(id);

    /// <summary>
    /// Get the given entity by id.
    /// </summary>
    /// <param name="id">The id of the entity.</param>
    /// <typeparam name="TEntity">The type of the entity.</typeparam>
    /// <returns>The entity.</returns>
    /// <exception cref="Exception">If the entity is not of the specified type.</exception>
    public TEntity? GetEntity<TEntity>(long id)
    {
        var entity = GetEntity(id);
        if (entity is null)
        {
            return default;
        }

        if (entity is TEntity tentity)
        {
            return tentity;
        }

        throw new Exception($"Could not find the entity with the given type {typeof(TEntity)}, was {entity.GetType()}");
    }

    /// <summary>
    /// Add the given entity to the entities list.
    /// </summary>
    /// <param name="entity">The entity to add.</param>
    internal void AddEntity(IEntity entity)
    {
        _entities.AddOrUpdate(entity.Id, _ => entity, (i, e) => entity);
    }

    /// <summary>
    /// .
    /// </summary>
    /// <param name="entityId">The id of the entity.</param>
    /// <param name="createAction">The action to execute on create.</param>
    /// <param name="updateAction">The action to execute on update.</param>
    /// <typeparam name="TEntity">The type of the entity.</typeparam>
    internal void AddOrUpdateEntity<TEntity>
        (long entityId, Func<long, TEntity> createAction, Func<long, TEntity, TEntity> updateAction)
        where TEntity : IEntity
    {
        _entities.AddOrUpdate
            (entityId, (key) => createAction(key), (key, entity) => updateAction(key, (TEntity)entity));
    }

    /// <summary>
    /// Remove the given entity.
    /// </summary>
    /// <param name="entity">The entity to remove.</param>
    internal void RemoveEntity(IEntity entity)
    {
        RemoveEntity(entity.Id);
    }

    /// <summary>
    /// Remove the given entity.
    /// </summary>
    /// <param name="entityId">The id of the entity to remove.</param>
    internal void RemoveEntity(long entityId)
    {
        _entities.TryRemove(entityId, out _);
    }
}
\ No newline at end of file

M Core/NosSmooth.Game/Data/Maps/Miniland.cs => Core/NosSmooth.Game/Data/Maps/Miniland.cs +18 -1
@@ 4,10 4,27 @@
//  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.Data.Abstractions.Infos;

namespace NosSmooth.Game.Data.Maps;

/// <summary>
/// Represents Miniland map that can contain miniland objects.
/// </summary>
/// <param name="Objects">The objects in the miniland.</param>
public record Miniland(IReadOnlyList<MinilandObject>? Objects) : Map;
\ No newline at end of file
public record Miniland
(
    long Id,
    byte Type,
    IMapInfo? Info,
    MapEntities Entities,
    IReadOnlyList<Portal> Portals,
    IReadOnlyList<MinilandObject>? Objects
) : Map
(
    Id,
    Type,
    Info,
    Entities,
    Portals
);
\ No newline at end of file

M Core/NosSmooth.Game/Data/Maps/Portal.cs => Core/NosSmooth.Game/Data/Maps/Portal.cs +10 -1
@@ 5,12 5,21 @@
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using NosSmooth.Game.Data.Info;
using NosSmooth.Packets.Enums;

namespace NosSmooth.Game.Data.Maps;

/// <summary>
/// Represents map portal leading to another map.
/// </summary>
/// <param name="PortalId">The portal id.</param>
/// <param name="Position">The position of the portal.</param>
/// <param name="TargetMapId">The id of the target map.</param>
public record Portal(Position Position, long TargetMapId);
\ No newline at end of file
public record Portal
(
    long PortalId,
    Position Position,
    long TargetMapId,
    PortalType? PortalType,
    bool IsDisabled
);
\ No newline at end of file

M Core/NosSmooth.Game/Data/Social/Family.cs => Core/NosSmooth.Game/Data/Social/Family.cs +8 -1
@@ 12,4 12,11 @@ namespace NosSmooth.Game.Data.Social;
/// <param name="Id">The id of the family.</param>
/// <param name="Name">The name of the family.</param>
/// <param name="Level">The level of the entity.</param>
public record Family(string? Id, string? Name, byte? Level);
\ No newline at end of file
public record Family
(
    string? Id,
    short? Title,
    string? Name,
    byte? Level,
    IReadOnlyList<bool>? Icons
);
\ No newline at end of file

Do not follow this link