M Core/NosSmooth.Game/Data/Entities/GroundItem.cs => Core/NosSmooth.Game/Data/Entities/GroundItem.cs +1 -1
@@ 18,7 18,7 @@ public class GroundItem : IEntity
/// <summary>
/// Gets or sets the id of the owner, if any.
/// </summary>
- public long? OwnerId { get; set; }
+ public long? OwnerId { get; internal set; }
/// <summary>
/// Gets or sets the amount of the item on the ground.
A Core/NosSmooth.Game/Data/Items/PartnerEquipment.cs => Core/NosSmooth.Game/Data/Items/PartnerEquipment.cs +22 -0
@@ 0,0 1,22 @@
+//
+// PartnerEquipment.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;
+
+/// <summary>
+/// An equipment of a partner.
+/// </summary>
+/// <param name="Weapon">The weapon of the partner.</param>
+/// <param name="Armor">The armor of the partner.</param>
+/// <param name="Gauntlet">The gauntlet of the partner.</param>
+/// <param name="Boots">The boots of the partner.</param>
+public record PartnerEquipment
+(
+ UpgradeableItem? Weapon,
+ UpgradeableItem? Armor,
+ UpgradeableItem? Gauntlet,
+ UpgradeableItem? Boots
+);<
\ No newline at end of file
A Core/NosSmooth.Game/Data/Mates/Mate.cs => Core/NosSmooth.Game/Data/Mates/Mate.cs +48 -0
@@ 0,0 1,48 @@
+//
+// Mate.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 NosSmooth.Game.Data.Stats;
+using NosSmooth.Packets.Enums;
+using NosSmooth.PacketSerializer.Abstractions.Common;
+
+namespace NosSmooth.Game.Data.Mates;
+
+/// <summary>
+/// Information about player's pet or partner.
+/// </summary>
+/// <remarks>
+/// Used for mates the character owns
+/// </remarks>
+/// <param name="MateId">The id of the mate.</param>
+/// <param name="NpcVNum">The vnum of the mate.</param>
+/// <param name="TransportId">Unknown function TODO.</param>
+/// <param name="Level">The level of the mate.</param>
+/// <param name="Loyalty">The loyalty of the mate.</param>
+/// <param name="Attack">The attack statistics of the mate.</param>
+/// <param name="Armor">The armor statistics of the mate.</param>
+/// <param name="Element">The element of the mate.</param>
+/// <param name="Resistance">The resistance of the mate.</param>
+/// <param name="Hp">The health of the mate.</param>
+/// <param name="Mp">The mana of the mate.</param>
+/// <param name="Name">The name of the mate.</param>
+/// <param name="IsSummonable">Whether the mate is summonable.</param>
+public record Mate
+(
+ long MateId,
+ long NpcVNum,
+ long TransportId,
+ Level Level,
+ short Loyalty,
+ MateAttackStats Attack,
+ MateArmorStats Armor,
+ Element Element,
+ Resistance Resistance,
+ Health Hp,
+ Health Mp,
+ string Name,
+ bool IsSummonable
+);<
\ No newline at end of file
A Core/NosSmooth.Game/Data/Mates/Mates.cs => Core/NosSmooth.Game/Data/Mates/Mates.cs +94 -0
@@ 0,0 1,94 @@
+//
+// Mates.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;
+using System.Collections.Concurrent;
+using NosSmooth.Game.Data.Characters;
+
+namespace NosSmooth.Game.Data.Mates;
+
+/// <summary>
+/// Game mates state.
+/// </summary>
+public class Mates : IEnumerable<Mate>
+{
+ private ConcurrentDictionary<long, Partner> _partners;
+ private ConcurrentDictionary<long, Pet> _pets;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Mates"/> class.
+ /// </summary>
+ public Mates()
+ {
+ _partners = new ConcurrentDictionary<long, Partner>();
+ _pets = new ConcurrentDictionary<long, Pet>();
+ }
+
+ /// <summary>
+ /// Gets all of the partners belonging to the character.
+ /// </summary>
+ public IEnumerable<Partner> Partners => _partners.Values;
+
+ /// <summary>
+ /// Gets all of the pets belonging to the character.
+ /// </summary>
+ public IEnumerable<Pet> Pets => _pets.Values;
+
+ /// <summary>
+ /// Gets the current skill of pet, if there is any.
+ /// </summary>
+ public Skill? PetSkill { get; internal set; }
+
+ /// <summary>
+ /// Get sthe current skills of partner(' sp).
+ /// </summary>
+ public IReadOnlyList<Skill>? PartnerSkills { get; internal set; }
+
+ /// <summary>
+ /// Gets the current pet of the client.
+ /// </summary>
+ public PartyPet? CurrentPet { get; internal set; }
+
+ /// <summary>
+ /// Gets the current partner of the client.
+ /// </summary>
+ public PartyPartner? CurrentPartner { get; internal set; }
+
+ /// <inheritdoc />
+ public IEnumerator<Mate> GetEnumerator()
+ => _partners.Values.Cast<Mate>().Concat(_pets.Values.Cast<Mate>()).GetEnumerator();
+
+ /// <inheritdoc/>
+ IEnumerator IEnumerable.GetEnumerator()
+ => GetEnumerator();
+
+ /// <summary>
+ /// Sets pet of the given id.
+ /// </summary>
+ /// <param name="pet">The pet.</param>
+ internal void SetPet(Pet pet)
+ {
+ _pets[pet.MateId] = pet;
+ }
+
+ /// <summary>
+ /// Sets partner of the given id.
+ /// </summary>
+ /// <param name="partner">The partner.</param>
+ internal void SetPartner(Partner partner)
+ {
+ _partners[partner.MateId] = partner;
+ }
+
+ /// <summary>
+ /// Clears partners and pets.
+ /// </summary>
+ internal void Clear()
+ {
+ _partners.Clear();
+ _pets.Clear();
+ }
+}<
\ No newline at end of file
A Core/NosSmooth.Game/Data/Mates/Partner.cs => Core/NosSmooth.Game/Data/Mates/Partner.cs +66 -0
@@ 0,0 1,66 @@
+//
+// Partner.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 NosSmooth.Game.Data.Items;
+using NosSmooth.Game.Data.Stats;
+using NosSmooth.Packets.Enums;
+
+namespace NosSmooth.Game.Data.Mates;
+
+/// <summary>
+/// Information about player's partner
+/// </summary>
+/// <param name="MateId">The id of the mate.</param>
+/// <param name="NpcVNum">The vnum of the mate.</param>
+/// <param name="TransportId">Unknown function TODO.</param>
+/// <param name="Level">The level of the mate.</param>
+/// <param name="Loyalty">The loyalty of the mate.</param>
+/// <param name="Attack">The attack statistics of the mate.</param>
+/// <param name="Armor">The armor statistics of the mate.</param>
+/// <param name="Equipment">The equipment of the partner.</param>
+/// <param name="Element">The element of the mate.</param>
+/// <param name="Resistance">The resistance of the mate.</param>
+/// <param name="Hp">The health of the mate.</param>
+/// <param name="Mp">The mana of the mate.</param>
+/// <param name="MorphVNum">The morph vnum of the partner.</param>
+/// <param name="Name">The name of the mate.</param>
+/// <param name="IsSummonable">Whether the mate is summonable.</param>
+/// <param name="Sp">The equipped sp of the partner.</param>
+public record Partner
+(
+ long MateId,
+ long NpcVNum,
+ long TransportId,
+ Level Level,
+ short Loyalty,
+ MateAttackStats Attack,
+ MateArmorStats Armor,
+ PartnerEquipment Equipment,
+ Element Element,
+ Resistance Resistance,
+ Health Hp,
+ Health Mp,
+ string Name,
+ int? MorphVNum,
+ bool IsSummonable,
+ PartnerSp? Sp
+) : Mate
+(
+ MateId,
+ NpcVNum,
+ TransportId,
+ Level,
+ Loyalty,
+ Attack,
+ Armor,
+ Element,
+ Resistance,
+ Hp,
+ Mp,
+ Name,
+ IsSummonable
+);<
\ No newline at end of file
A Core/NosSmooth.Game/Data/Mates/PartnerSkill.cs => Core/NosSmooth.Game/Data/Mates/PartnerSkill.cs +24 -0
@@ 0,0 1,24 @@
+//
+// PartnerSkill.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.Game.Data.Characters;
+using NosSmooth.Packets.Enums.Mates;
+
+namespace NosSmooth.Game.Data.Mates;
+
+/// <summary>
+/// A skill of a partner's sp.
+/// </summary>
+/// <param name="SkillVNum">The vnum of the skill.</param>
+/// <param name="Rank">The partner rank of the skill.</param>
+/// <param name="Info">The info of the skill.</param>
+public record PartnerSkill
+(
+ int SkillVNum,
+ PartnerSkillRank? Rank,
+ ISkillInfo? Info
+) : Skill(SkillVNum, null, Info);<
\ No newline at end of file
A Core/NosSmooth.Game/Data/Mates/PartnerSp.cs => Core/NosSmooth.Game/Data/Mates/PartnerSp.cs +26 -0
@@ 0,0 1,26 @@
+//
+// PartnerSp.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.Characters;
+
+namespace NosSmooth.Game.Data.Mates;
+
+/// <summary>
+/// An sp of the partner.
+/// </summary>
+/// <param name="VNum">The vnum of the sp item.</param>
+/// <param name="AgilityPercentage">The agility percentage for acquiring skills.</param>
+/// <param name="Skill1">Information about the first skill of the partner.</param>
+/// <param name="Skill2">Information about the second skill of the partner.</param>
+/// <param name="Skill3">Information about the third skill of the partner.</param>
+public record PartnerSp
+(
+ long VNum,
+ byte? AgilityPercentage,
+ PartnerSkill? Skill1,
+ PartnerSkill? Skill2,
+ PartnerSkill? Skill3
+);<
\ No newline at end of file
A Core/NosSmooth.Game/Data/Mates/PartyPartner.cs => Core/NosSmooth.Game/Data/Mates/PartyPartner.cs +29 -0
@@ 0,0 1,29 @@
+//
+// PartyPartner.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;
+
+namespace NosSmooth.Game.Data.Mates;
+
+/// <summary>
+/// Information about a partner that is currently in character's party.
+/// </summary>
+/// <param name="Partner">The underlying partner.</param>
+public record PartyPartner
+(
+ Partner Partner
+)
+{
+ /// <summary>
+ /// Gets the hp of the partner.
+ /// </summary>
+ public Health? Hp { get; internal set; }
+
+ /// <summary>
+ /// Gets the mp of the partner.
+ /// </summary>
+ public Health? Mp { get; internal set; }
+}<
\ No newline at end of file
A Core/NosSmooth.Game/Data/Mates/PartyPet.cs => Core/NosSmooth.Game/Data/Mates/PartyPet.cs +29 -0
@@ 0,0 1,29 @@
+//
+// PartyPet.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;
+
+namespace NosSmooth.Game.Data.Mates;
+
+/// <summary>
+/// Information about a pet that is currently in character's party.
+/// </summary>
+/// <param name="Pet">The underlying pet.</param>
+public record PartyPet
+(
+ Pet Pet
+)
+{
+ /// <summary>
+ /// Gets the hp of the partner.
+ /// </summary>
+ public Health? Hp { get; internal set; }
+
+ /// <summary>
+ /// Gets the mp of the partner.
+ /// </summary>
+ public Health? Mp { get; internal set; }
+}<
\ No newline at end of file
A Core/NosSmooth.Game/Data/Mates/Pet.cs => Core/NosSmooth.Game/Data/Mates/Pet.cs +66 -0
@@ 0,0 1,66 @@
+//
+// Pet.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 NosSmooth.Game.Data.Items;
+using NosSmooth.Game.Data.Stats;
+using NosSmooth.Packets.Enums;
+using NosSmooth.PacketSerializer.Abstractions.Common;
+
+namespace NosSmooth.Game.Data.Mates;
+
+/// <summary>
+/// Information about player's pet or partner.
+/// </summary>
+/// <remarks>
+/// Used for mates the character owns
+/// </remarks>
+/// <param name="MateId">The id of the mate.</param>
+/// <param name="NpcVNum">The vnum of the mate.</param>
+/// <param name="TransportId">Unknown function TODO.</param>
+/// <param name="Level">The level of the mate.</param>
+/// <param name="Loyalty">The loyalty of the mate.</param>
+/// <param name="Attack">The attack statistics of the mate.</param>
+/// <param name="Armor">The armor statistics of the mate.</param>
+/// <param name="Element">The element of the mate.</param>
+/// <param name="Resistance">The resistance of the mate.</param>
+/// <param name="Hp">The health of the mate.</param>
+/// <param name="Mp">The mana of the mate.</param>
+/// <param name="Name">The name of the mate.</param>
+/// <param name="IsSummonable">Whether the mate is summonable.</param>
+/// <param name="CanPickUp">Whether the pet can pick up items.</param>
+public record Pet
+(
+ long MateId,
+ long NpcVNum,
+ long TransportId,
+ Level Level,
+ short Loyalty,
+ MateAttackStats Attack,
+ MateArmorStats Armor,
+ Element Element,
+ Resistance Resistance,
+ Health Hp,
+ Health Mp,
+ string Name,
+ bool IsSummonable,
+ bool CanPickUp
+) : Mate
+(
+ MateId,
+ NpcVNum,
+ TransportId,
+ Level,
+ Loyalty,
+ Attack,
+ Armor,
+ Element,
+ Resistance,
+ Hp,
+ Mp,
+ Name,
+ IsSummonable
+);<
\ No newline at end of file
M Core/NosSmooth.Game/Data/Social/Group.cs => Core/NosSmooth.Game/Data/Social/Group.cs +0 -1
@@ 16,7 16,6 @@ namespace NosSmooth.Game.Data.Social;
/// Represents nostale group of players or pets and partners.
/// </summary>
/// <param name="Id">The id of the group.</param>
-/// <param name="Size">The size of the group.</param>
/// <param name="Members">The members of the group. (excluding the character)</param>
public record Group(short? Id, IReadOnlyList<GroupMember>? Members)
{
M Core/NosSmooth.Game/Data/Social/GroupMember.cs => Core/NosSmooth.Game/Data/Social/GroupMember.cs +5 -1
@@ 9,6 9,10 @@ using NosSmooth.Packets.Enums.Players;
namespace NosSmooth.Game.Data.Social;
+/// <summary>
+/// A member of a group the character is in.
+/// </summary>
+/// <param name="PlayerId">The id of the group member player.</param>
public record GroupMember(long PlayerId)
{
/// <summary>
@@ 54,5 58,5 @@ public record GroupMember(long PlayerId)
/// <summary>
/// Gets the effects of the member.
/// </summary>
- public IReadOnlyList<long>? EffectsVNums { get; internal set; }
+ public IReadOnlyList<short>? EffectsVNums { get; internal set; }
}=
\ No newline at end of file
A Core/NosSmooth.Game/Data/Stats/MateArmorStats.cs => Core/NosSmooth.Game/Data/Stats/MateArmorStats.cs +26 -0
@@ 0,0 1,26 @@
+//
+// MateArmorStats.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.Stats;
+
+/// <summary>
+/// Stats about mate armor.
+/// </summary>
+/// <param name="DefenceUpgrade">The upgrade of defence.</param>
+/// <param name="MeleeDefence">The melee defence.</param>
+/// <param name="MeleeDefenceDodge">The melee dodge rate.</param>
+/// <param name="RangedDefence">The ranged defence.</param>
+/// <param name="RangedDodgeRate">The ranged dodge rate.</param>
+/// <param name="MagicalDefence">The magical defence.</param>
+public record MateArmorStats
+(
+ short DefenceUpgrade,
+ int MeleeDefence,
+ int MeleeDefenceDodge,
+ int RangedDefence,
+ int RangedDodgeRate,
+ int MagicalDefence
+);<
\ No newline at end of file
A Core/NosSmooth.Game/Data/Stats/MateAttackStats.cs => Core/NosSmooth.Game/Data/Stats/MateAttackStats.cs +26 -0
@@ 0,0 1,26 @@
+//
+// MateAttackStats.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.Stats;
+
+/// <summary>
+/// Stats about mate attack.
+/// </summary>
+/// <param name="AttackUpgrade">The upgrade of attack.</param>
+/// <param name="MinimumAttack">The minimum attack.</param>
+/// <param name="MaximumAttack">The maximum attack.</param>
+/// <param name="Precision">The precision or concentration.</param>
+/// <param name="CriticalChance">The critical chance.</param>
+/// <param name="CriticalRate">The critical rate.</param>
+public record MateAttackStats
+(
+ short AttackUpgrade,
+ int MinimumAttack,
+ int MaximumAttack,
+ int Precision,
+ int CriticalChance,
+ int CriticalRate
+);<
\ No newline at end of file
A Core/NosSmooth.Game/Data/Stats/Resistance.cs => Core/NosSmooth.Game/Data/Stats/Resistance.cs +22 -0
@@ 0,0 1,22 @@
+//
+// Resistance.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.Stats;
+
+/// <summary>
+/// Stats about resistance of character or mate.
+/// </summary>
+/// <param name="FireResistance">The fire resistance percentage.</param>
+/// <param name="WaterResistance">The water resistance percentage.</param>
+/// <param name="LightResistance">The light resistance percentage.</param>
+/// <param name="DarkResistance">The dark resistance percentage.</param>
+public record Resistance
+(
+ short FireResistance,
+ short WaterResistance,
+ short LightResistance,
+ short DarkResistance
+);<
\ No newline at end of file
M Core/NosSmooth.Game/Events/Groups/GroupInitializedEvent.cs => Core/NosSmooth.Game/Events/Groups/GroupInitializedEvent.cs +7 -0
@@ 8,4 8,11 @@ using NosSmooth.Game.Data.Social;
namespace NosSmooth.Game.Events.Groups;
+/// <summary>
+/// A group has been initialized.
+/// </summary>
+/// <remarks>
+/// May be sent multiple times even for the same group.
+/// </remarks>
+/// <param name="Group">The initialized group with members.</param>
public record GroupInitializedEvent(Group Group) : IGameEvent;=
\ No newline at end of file
M Core/NosSmooth.Game/Events/Groups/GroupMemberStatEvent.cs => Core/NosSmooth.Game/Events/Groups/GroupMemberStatEvent.cs +4 -0
@@ 8,4 8,8 @@ using NosSmooth.Game.Data.Social;
namespace NosSmooth.Game.Events.Groups;
+/// <summary>
+/// A new stats (hp, mp) of a group received.
+/// </summary>
+/// <param name="Member">The updated group member.</param>
public record GroupMemberStatEvent(GroupMember Member) : IGameEvent;=
\ No newline at end of file
A Core/NosSmooth.Game/Events/Mates/MateStatEvent.cs => Core/NosSmooth.Game/Events/Mates/MateStatEvent.cs +19 -0
@@ 0,0 1,19 @@
+//
+// MateStatEvent.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 NosSmooth.Game.Data.Mates;
+using NosSmooth.Game.Data.Social;
+
+namespace NosSmooth.Game.Events.Mates;
+
+/// <summary>
+/// A new stats (hp, mp) of a mate received.
+/// </summary>
+/// <param name="Mate">The mate.</param>
+/// <param name="Hp">The current hp of the mate.</param>
+/// <param name="Mp">The current mp of the mate.</param>
+public record MateStatEvent(Mate Mate, Health Hp, Health Mp) : IGameEvent;<
\ No newline at end of file
A Core/NosSmooth.Game/Events/Mates/MatesPartyInitializedEvent.cs => Core/NosSmooth.Game/Events/Mates/MatesPartyInitializedEvent.cs +17 -0
@@ 0,0 1,17 @@
+//
+// MatesPartyInitializedEvent.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.Mates;
+using NosSmooth.Game.Data.Social;
+
+namespace NosSmooth.Game.Events.Mates;
+
+/// <summary>
+/// A party was initialized and got information about currently present party pets.
+/// </summary>
+/// <param name="Pet">The party pet.</param>
+/// <param name="Partner">The party partner.</param>
+public record MatesPartyInitializedEvent(Pet? Pet, Partner? Partner) : IGameEvent;<
\ No newline at end of file
R Core/NosSmooth.Game/Data/Entities/Partner.cs => Core/NosSmooth.Game/Events/Mates/PartnerInitializedEvent.cs +8 -4
@@ 1,12 1,16 @@
//
-// Partner.cs
+// PartnerInitializedEvent.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.Entities;
+using NosSmooth.Game.Data.Mates;
+using NosSmooth.Game.Data.Social;
+
+namespace NosSmooth.Game.Events.Mates;
/// <summary>
-/// Represents Partner of the Character.
+/// A partner the character owns was initialized.
/// </summary>
-public record Partner() : IPet;>
\ No newline at end of file
+/// <param name="Partner">The partner.</param>
+public record PartnerInitializedEvent(Partner Partner) : IGameEvent;<
\ No newline at end of file
A Core/NosSmooth.Game/Events/Mates/PartnerSkillsReceivedEvent.cs => Core/NosSmooth.Game/Events/Mates/PartnerSkillsReceivedEvent.cs +18 -0
@@ 0,0 1,18 @@
+//
+// PartnerSkillsReceivedEvent.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.Characters;
+using NosSmooth.Game.Data.Mates;
+using NosSmooth.Game.Data.Social;
+
+namespace NosSmooth.Game.Events.Mates;
+
+/// <summary>
+/// A partner the character owns skills were received.
+/// </summary>
+/// <param name="Partner">The partner.</param>
+/// <param name="Skills">The skills of the partner.</param>
+public record PartnerSkillsReceivedEvent(Partner? Partner, IReadOnlyList<Skill> Skills) : IGameEvent;<
\ No newline at end of file
R Core/NosSmooth.Game/Data/Entities/Pet.cs => Core/NosSmooth.Game/Events/Mates/PetInitializedEvent.cs +8 -4
@@ 1,12 1,16 @@
//
-// Pet.cs
+// PetInitializedEvent.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.Entities;
+using NosSmooth.Game.Data.Mates;
+using NosSmooth.Game.Data.Social;
+
+namespace NosSmooth.Game.Events.Mates;
/// <summary>
-/// Represents pet of the character.
+/// A pet the character owns was initialized.
/// </summary>
-public record Pet() : IPet;>
\ No newline at end of file
+/// <param name="Pet">The pet.</param>
+public record PetInitializedEvent(Pet Pet) : IGameEvent;<
\ No newline at end of file
A Core/NosSmooth.Game/Events/Mates/PetSkillReceivedEvent.cs => Core/NosSmooth.Game/Events/Mates/PetSkillReceivedEvent.cs +18 -0
@@ 0,0 1,18 @@
+//
+// PetSkillReceivedEvent.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.Characters;
+using NosSmooth.Game.Data.Mates;
+using NosSmooth.Game.Data.Social;
+
+namespace NosSmooth.Game.Events.Mates;
+
+/// <summary>
+/// A pet the character owns skill was received.
+/// </summary>
+/// <param name="Pet">The pet.</param>
+/// <param name="PetSkill">The skill of the pet.</param>
+public record PetSkillReceivedEvent(Pet? Pet, Skill? PetSkill) : IGameEvent;<
\ No newline at end of file
R Core/NosSmooth.Game/Data/Entities/IPet.cs => Core/NosSmooth.Game/Extensions/LivingEntityExtensions.cs +15 -4
@@ 1,14 1,25 @@
//
-// IPet.cs
+// LivingEntityExtensions.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.Entities;
+using NosSmooth.Game.Data.Entities;
+
+namespace NosSmooth.Game.Extensions;
/// <summary>
-/// Represents base type for a pet or a partner.
+/// An extension methods for <see cref="ILivingEntity"/>.
/// </summary>
-public interface IPet
+public static class LivingEntityExtensions
{
+ /// <summary>
+ /// Checks whether the entity is alive.
+ /// </summary>
+ /// <param name="entity">The entity to check.</param>
+ /// <returns>Whether the entity is alive.</returns>
+ public static bool IsAlive(ILivingEntity entity)
+ {
+ return entity.Hp is null || entity.Hp.Amount != 0 || entity.Hp.Percentage != 0;
+ }
}=
\ No newline at end of file
M Core/NosSmooth.Game/Extensions/ServiceCollectionExtensions.cs => Core/NosSmooth.Game/Extensions/ServiceCollectionExtensions.cs +3 -0
@@ 15,6 15,7 @@ using NosSmooth.Game.PacketHandlers.Entities;
using NosSmooth.Game.PacketHandlers.Inventory;
using NosSmooth.Game.PacketHandlers.Map;
using NosSmooth.Game.PacketHandlers.Relations;
+using NosSmooth.Game.PacketHandlers.Skills;
using NosSmooth.Game.PacketHandlers.Specialists;
namespace NosSmooth.Game.Extensions;
@@ 40,11 41,13 @@ public static class ServiceCollectionExtensions
serviceCollection
.AddPacketResponder<CharacterInitResponder>()
.AddPacketResponder<PlayerSkillResponder>()
+ .AddPacketResponder<MatesSkillResponder>()
.AddPacketResponder<WalkResponder>()
.AddPacketResponder<SkillUsedResponder>()
.AddPacketResponder<FriendInitResponder>()
.AddPacketResponder<InventoryInitResponder>()
.AddPacketResponder<GroupInitResponder>()
+ .AddPacketResponder<MatesInitResponder>()
.AddPacketResponder<AoeSkillUsedResponder>()
.AddPacketResponder<AtResponder>()
.AddPacketResponder<CMapResponder>()
M Core/NosSmooth.Game/Game.cs => Core/NosSmooth.Game/Game.cs +35 -0
@@ 6,10 6,12 @@
using Microsoft.Extensions.Options;
using NosSmooth.Core.Stateful;
+using NosSmooth.Game.Data;
using NosSmooth.Game.Data.Characters;
using NosSmooth.Game.Data.Chat;
using NosSmooth.Game.Data.Inventory;
using NosSmooth.Game.Data.Maps;
+using NosSmooth.Game.Data.Mates;
using NosSmooth.Game.Data.Raids;
using NosSmooth.Game.Data.Social;
@@ 44,6 46,11 @@ public class Game : IStatefulEntity
public Character? Character { get; internal set; }
/// <summary>
+ /// Gets the mates of the current character.
+ /// </summary>
+ public Mates? Mates { get; internal set; }
+
+ /// <summary>
/// Gets or sets the inventory of the character.
/// </summary>
public Inventory? Inventory { get; internal set; }
@@ 89,6 96,34 @@ public class Game : IStatefulEntity
public Raid? CurrentRaid { get; internal set; }
/// <summary>
+ /// Creates the mates if they are null, or updates the current mates.
+ /// </summary>
+ /// <param name="create">The function for creating the mates.</param>
+ /// <param name="update">The function for updating the mates.</param>
+ /// <param name="releaseSemaphore">Whether to release the semaphore used for changing the mates.</param>
+ /// <param name="ct">The cancellation token for cancelling the operation.</param>
+ /// <returns>The updated mates.</returns>
+ internal async Task<Mates?> CreateOrUpdateMatesAsync
+ (
+ Func<Mates?> create,
+ Func<Mates, Mates?> update,
+ bool releaseSemaphore = true,
+ CancellationToken ct = default
+ )
+ {
+ return await CreateOrUpdateAsync
+ (
+ GameSemaphoreType.Mates,
+ () => Mates,
+ s => Mates = s,
+ create,
+ update,
+ releaseSemaphore,
+ ct
+ );
+ }
+
+ /// <summary>
/// Creates the skills if they are null, or updates the current skills.
/// </summary>
/// <param name="create">The function for creating the skills.</param>
M Core/NosSmooth.Game/GameSemaphoreType.cs => Core/NosSmooth.Game/GameSemaphoreType.cs +6 -1
@@ 49,5 49,10 @@ public enum GameSemaphoreType
/// <summary>
/// The semaphore for raid.
/// </summary>
- Raid
+ Raid,
+
+ /// <summary>
+ /// The semaphore for mates.
+ /// </summary>
+ Mates
}=
\ No newline at end of file
M Core/NosSmooth.Game/PacketHandlers/Relations/FriendInitResponder.cs => Core/NosSmooth.Game/PacketHandlers/Relations/FriendInitResponder.cs +4 -3
@@ 40,10 40,11 @@ public class FriendInitResponder : IPacketResponder<FInfoPacket>, IPacketRespond
.Select(
x =>
{
- if (x.PlayerId == packet.PlayerId)
+ var subPacket = packet.FriendSubPackets.FirstOrDefault(y => x.PlayerId == y.PlayerId);
+ if (subPacket is not null)
{
- x.IsConnected = packet.IsConnected;
- x.CharacterName = packet.Name;
+ x.IsConnected = subPacket.IsConnected;
+ x.CharacterName = subPacket.Name;
}
return x;
A Core/NosSmooth.Game/PacketHandlers/Relations/MatesInitResponder.cs => Core/NosSmooth.Game/PacketHandlers/Relations/MatesInitResponder.cs +367 -0
@@ 0,0 1,367 @@
+//
+// MatesInitResponder.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 Microsoft.Extensions.Logging;
+using NosSmooth.Core.Extensions;
+using NosSmooth.Core.Packets;
+using NosSmooth.Data.Abstractions;
+using NosSmooth.Game.Data.Characters;
+using NosSmooth.Game.Data.Info;
+using NosSmooth.Game.Data.Items;
+using NosSmooth.Game.Data.Mates;
+using NosSmooth.Game.Data.Social;
+using NosSmooth.Game.Data.Stats;
+using NosSmooth.Game.Events.Core;
+using NosSmooth.Game.Events.Mates;
+using NosSmooth.Packets.Enums.Entities;
+using NosSmooth.Packets.Enums.Mates;
+using NosSmooth.Packets.Server.Groups;
+using NosSmooth.Packets.Server.Mates;
+using NosSmooth.Packets.Server.Miniland;
+using Remora.Results;
+
+namespace NosSmooth.Game.PacketHandlers.Relations;
+
+/// <summary>
+/// A mates initialization responder.
+/// </summary>
+public class MatesInitResponder : IPacketResponder<ScPPacket>, IPacketResponder<ScNPacket>,
+ IPacketResponder<PinitPacket>, IPacketResponder<PstPacket>, IPacketResponder<PClearPacket>
+{
+ private readonly Game _game;
+ private readonly IInfoService _infoService;
+ private readonly EventDispatcher _eventDispatcher;
+ private readonly ILogger<MatesInitResponder> _logger;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MatesInitResponder"/> class.
+ /// </summary>
+ /// <param name="game">The game.</param>
+ /// <param name="infoService">The info service.</param>
+ /// <param name="eventDispatcher">The event dispatcher.</param>
+ /// <param name="logger">The logger.</param>
+ public MatesInitResponder
+ (
+ Game game,
+ IInfoService infoService,
+ EventDispatcher eventDispatcher,
+ ILogger<MatesInitResponder> logger
+ )
+ {
+ _game = game;
+ _infoService = infoService;
+ _eventDispatcher = eventDispatcher;
+ _logger = logger;
+ }
+
+ /// <inheritdoc />
+ public async Task<Result> Respond(PacketEventArgs<ScPPacket> packetArgs, CancellationToken ct = default)
+ {
+ var packet = packetArgs.Packet;
+ var pet = new Pet
+ (
+ packet.PetId,
+ packet.NpcVNum,
+ packet.TransportId,
+ new Level(packet.Level, packet.Experience, packet.LevelExperience),
+ packet.Loyalty,
+ new MateAttackStats
+ (
+ packet.AttackUpgrade,
+ packet.MinimumAttack,
+ packet.MaximumAttack,
+ packet.Concentrate,
+ packet.CriticalChance,
+ packet.CriticalRate
+ ),
+ new MateArmorStats
+ (
+ packet.DefenceUpgrade,
+ packet.MeleeDefence,
+ packet.MeleeDefenceDodge,
+ packet.RangeDefence,
+ packet.RangeDodgeRate,
+ packet.MagicalDefence
+ ),
+ packet.Element,
+ new Resistance
+ (
+ packet.ResistanceSubPacket.FireResistance,
+ packet.ResistanceSubPacket.WaterResistance,
+ packet.ResistanceSubPacket.LightResistance,
+ packet.ResistanceSubPacket.DarkResistance
+ ),
+ new Health { Amount = packet.Hp, Maximum = packet.HpMax },
+ new Health { Amount = packet.Mp, Maximum = packet.MpMax },
+ packet.Name,
+ packet.IsSummonable,
+ packet.CanPickUp
+ );
+
+ await _game.CreateOrUpdateMatesAsync
+ (
+ () =>
+ {
+ var mates = new Mates();
+ mates.SetPet(pet);
+ return mates;
+ },
+ mates =>
+ {
+ mates.SetPet(pet);
+ return mates;
+ },
+ ct: ct
+ );
+
+ return await _eventDispatcher.DispatchEvent
+ (
+ new PetInitializedEvent(pet),
+ ct
+ );
+ }
+
+ /// <inheritdoc />
+ public async Task<Result> Respond(PacketEventArgs<ScNPacket> packetArgs, CancellationToken ct = default)
+ {
+ var packet = packetArgs.Packet;
+ var partner = new Partner
+ (
+ packet.PartnerId,
+ packet.NpcVNum,
+ packet.TransportId,
+ new Level(packet.Level, packet.Experience, packet.LevelExperience),
+ packet.Loyalty,
+ new MateAttackStats
+ (
+ packet.AttackUpgrade,
+ packet.MinimumAttack,
+ packet.MaximumAttack,
+ packet.Precision,
+ packet.CriticalChance,
+ packet.CriticalRate
+ ),
+ new MateArmorStats
+ (
+ packet.DefenceUpgrade,
+ packet.MeleeDefence,
+ packet.MeleeDefenceDodge,
+ packet.RangeDefence,
+ packet.RangeDodgeRate,
+ packet.MagicalDefence
+ ),
+ new PartnerEquipment
+ (
+ await CreatePartnerItem(packet.WeaponSubPacket, ct),
+ await CreatePartnerItem(packet.ArmorSubPacket, ct),
+ await CreatePartnerItem(packet.GauntletSubPacket, ct),
+ await CreatePartnerItem(packet.BootsSubPacket, ct)
+ ),
+ packet.Element,
+ new Resistance
+ (
+ packet.ResistanceSubPacket.FireResistance,
+ packet.ResistanceSubPacket.WaterResistance,
+ packet.ResistanceSubPacket.LightResistance,
+ packet.ResistanceSubPacket.DarkResistance
+ ),
+ new Health { Amount = packet.Hp, Maximum = packet.HpMax },
+ new Health { Amount = packet.Mp, Maximum = packet.MpMax },
+ packet.Name,
+ packet.MorphVNum,
+ packet.IsSummonable,
+#pragma warning disable SA1118
+ packet.SpSubPacket.Value is not null
+ ? new PartnerSp
+ (
+ packet.SpSubPacket.Value.ItemVNum,
+ packet.SpSubPacket.Value.AgilityPercentage,
+ await CreateSkill(packet.Skill1SubPacket, ct),
+ await CreateSkill(packet.Skill2SubPacket, ct),
+ await CreateSkill(packet.Skill3SubPacket, ct)
+ )
+ : null
+#pragma warning restore SA1118
+ );
+
+ await _game.CreateOrUpdateMatesAsync
+ (
+ () =>
+ {
+ var mates = new Mates();
+ mates.SetPartner(partner);
+ return mates;
+ },
+ mates =>
+ {
+ mates.SetPartner(partner);
+ return mates;
+ },
+ ct: ct
+ );
+
+ return await _eventDispatcher.DispatchEvent
+ (
+ new PartnerInitializedEvent(partner),
+ ct
+ );
+ }
+
+ /// <inheritdoc />
+ public async Task<Result> Respond(PacketEventArgs<PinitPacket> packetArgs, CancellationToken ct = default)
+ {
+ var packet = packetArgs.Packet;
+ var partner = packet.PinitSubPackets?.FirstOrDefault(x => x.MateSubPacket?.MateType == MateType.Partner);
+ var pet = packet.PinitSubPackets?.FirstOrDefault(x => x.MateSubPacket?.MateType == MateType.Pet);
+
+ Partner? gamePartner = null;
+ Pet? gamePet = null;
+
+ await _game.CreateOrUpdateMatesAsync
+ (
+ () => null,
+ m =>
+ {
+ if (partner is not null)
+ {
+ gamePartner = _game.Mates?.Partners.FirstOrDefault(x => x.MateId == partner.EntityId);
+ if (gamePartner is not null && gamePartner != m.CurrentPartner?.Partner)
+ {
+ m.CurrentPartner = new PartyPartner(gamePartner);
+ }
+ }
+
+ if (pet is not null)
+ {
+ gamePet = _game.Mates?.Pets.FirstOrDefault(x => x.MateId == pet.EntityId);
+ if (gamePet is not null && gamePet != m.CurrentPet?.Pet)
+ {
+ m.CurrentPet = new PartyPet(gamePet);
+ }
+ }
+
+ return m;
+ },
+ ct: ct
+ );
+
+ return await _eventDispatcher.DispatchEvent
+ (
+ new MatesPartyInitializedEvent(gamePet, gamePartner),
+ ct
+ );
+ }
+
+ /// <inheritdoc />
+ public async Task<Result> Respond(PacketEventArgs<PstPacket> packetArgs, CancellationToken ct = default)
+ {
+ var packet = packetArgs.Packet;
+ if (packet.EntityType != EntityType.Npc || packet.MateType is null)
+ {
+ return Result.FromSuccess();
+ }
+
+ Mate? mate = null;
+ var hp = new Health { Amount = packet.Hp, Percentage = packet.HpPercentage };
+ var mp = new Health { Amount = packet.Mp, Percentage = packet.MpPercentage };
+
+ await _game.CreateOrUpdateMatesAsync
+ (
+ () => null,
+ m =>
+ {
+ if (packet.MateType is MateType.Pet && m.CurrentPet is not null)
+ {
+ mate = m.CurrentPet.Pet;
+ m.CurrentPet.Hp = hp;
+ m.CurrentPet.Mp = mp;
+ }
+ else if (packet.MateType is MateType.Partner && m.CurrentPartner is not null)
+ {
+ mate = m.CurrentPartner.Partner;
+ m.CurrentPartner.Hp = hp;
+ m.CurrentPartner.Mp = mp;
+ }
+
+ return m;
+ },
+ ct: ct
+ );
+
+ if (mate is not null)
+ {
+ return await _eventDispatcher.DispatchEvent(new MateStatEvent(mate, hp, mp), ct);
+ }
+
+ return Result.FromSuccess();
+ }
+
+ /// <inheritdoc />
+ public async Task<Result> Respond(PacketEventArgs<PClearPacket> packetArgs, CancellationToken ct = default)
+ {
+ await _game.CreateOrUpdateMatesAsync
+ (
+ () => null,
+ m =>
+ {
+ m.Clear();
+ return m;
+ },
+ ct: ct
+ );
+
+ return Result.FromSuccess();
+ }
+
+ private async Task<UpgradeableItem?> CreatePartnerItem(ScNEquipmentSubPacket? packet, CancellationToken ct)
+ {
+ if (packet is null)
+ {
+ return null;
+ }
+
+ var itemInfoResult = await _infoService.GetItemInfoAsync(packet.ItemVNum, ct);
+ if (!itemInfoResult.IsDefined(out var itemInfo))
+ {
+ _logger.LogWarning
+ (
+ "Could not obtain an item info for vnum {vnum}: {error}",
+ packet.ItemVNum,
+ itemInfoResult.ToFullString()
+ );
+ }
+
+ return new UpgradeableItem
+ (
+ packet.ItemVNum,
+ itemInfo,
+ packet.ItemUpgrade,
+ packet.ItemRare,
+ 0
+ );
+ }
+
+ private async Task<PartnerSkill?> CreateSkill(ScNSkillSubPacket? packet, CancellationToken ct)
+ {
+ if (packet is null)
+ {
+ return null;
+ }
+
+ var skillInfoResult = await _infoService.GetSkillInfoAsync(packet.SkillVNum, ct);
+ if (!skillInfoResult.IsDefined(out var skillInfo))
+ {
+ _logger.LogWarning
+ (
+ "Could not obtain a skill info for vnum {vnum}: {error}",
+ packet.SkillVNum,
+ skillInfoResult.ToFullString()
+ );
+ }
+
+ return new PartnerSkill(packet.SkillVNum, packet.Rank, skillInfo);
+ }
+}<
\ No newline at end of file
A Core/NosSmooth.Game/PacketHandlers/Skills/MatesSkillResponder.cs => Core/NosSmooth.Game/PacketHandlers/Skills/MatesSkillResponder.cs +145 -0
@@ 0,0 1,145 @@
+//
+// MatesSkillResponder.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 Microsoft.Extensions.Logging;
+using NosSmooth.Core.Extensions;
+using NosSmooth.Core.Packets;
+using NosSmooth.Data.Abstractions;
+using NosSmooth.Game.Data.Characters;
+using NosSmooth.Game.Data.Mates;
+using NosSmooth.Game.Events.Core;
+using NosSmooth.Game.Events.Mates;
+using NosSmooth.Packets.Server.Skills;
+using Remora.Results;
+
+namespace NosSmooth.Game.PacketHandlers.Skills;
+
+/// <summary>
+/// Responds to petski and pski packets.
+/// </summary>
+public class MatesSkillResponder : IPacketResponder<PetskiPacket>, IPacketResponder<PSkiPacket>
+{
+ private readonly Game _game;
+ private readonly EventDispatcher _eventDispatcher;
+ private readonly IInfoService _infoService;
+ private readonly ILogger<MatesSkillResponder> _logger;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MatesSkillResponder"/> class.
+ /// </summary>
+ /// <param name="game">The game.</param>
+ /// <param name="eventDispatcher">The event dispatcher.</param>
+ /// <param name="infoService">The info service.</param>
+ /// <param name="logger">The logger.</param>
+ public MatesSkillResponder
+ (
+ Game game,
+ EventDispatcher eventDispatcher,
+ IInfoService infoService,
+ ILogger<MatesSkillResponder> logger
+ )
+ {
+ _game = game;
+ _eventDispatcher = eventDispatcher;
+ _infoService = infoService;
+ _logger = logger;
+ }
+
+ /// <inheritdoc />
+ public async Task<Result> Respond(PacketEventArgs<PetskiPacket> packetArgs, CancellationToken ct = default)
+ {
+ var packet = packetArgs.Packet;
+ Skill? skill = null;
+ if (packet.SkillVNum is not null)
+ {
+ var skillInfoResult = await _infoService.GetSkillInfoAsync(packet.SkillVNum.Value, ct);
+ if (!skillInfoResult.IsDefined(out var skillInfo))
+ {
+ _logger.LogWarning
+ (
+ "Could not obtain skill info for vnum {vnum}: {error}",
+ packet.SkillVNum.Value,
+ skillInfoResult.ToFullString()
+ );
+ }
+
+ skill = new Skill(packet.SkillVNum.Value, null, skillInfo);
+ }
+
+ var mates = await _game.CreateOrUpdateMatesAsync
+ (
+ () => new Mates
+ {
+ PetSkill = skill
+ },
+ m =>
+ {
+ m.PetSkill = skill;
+ return m;
+ },
+ ct: ct
+ );
+
+ return await _eventDispatcher.DispatchEvent
+ (
+ new PetSkillReceivedEvent(mates?.CurrentPet?.Pet, skill),
+ ct
+ );
+ }
+
+ /// <inheritdoc />
+ public async Task<Result> Respond(PacketEventArgs<PSkiPacket> packetArgs, CancellationToken ct = default)
+ {
+ var packet = packetArgs.Packet;
+ var skills = new List<Skill>();
+
+ foreach (var skillVNum in packet.SkillVNums)
+ {
+ if (skillVNum is null)
+ {
+ continue;
+ }
+
+ var skillInfoResult = await _infoService.GetSkillInfoAsync(skillVNum.Value, ct);
+ if (!skillInfoResult.IsDefined(out var skillInfo))
+ {
+ _logger.LogWarning
+ (
+ "Could not obtain skill info for vnum {vnum}: {error}",
+ skillVNum,
+ skillInfoResult.ToFullString()
+ );
+ }
+
+ skills.Add(new Skill(skillVNum.Value, null, skillInfo));
+ }
+
+ if (skills.Count == 0)
+ {
+ skills = null;
+ }
+
+ var mates = await _game.CreateOrUpdateMatesAsync
+ (
+ () => new Mates
+ {
+ PartnerSkills = skills
+ },
+ m =>
+ {
+ m.PartnerSkills = skills;
+ return m;
+ },
+ ct: ct
+ );
+
+ return await _eventDispatcher.DispatchEvent
+ (
+ new PartnerSkillsReceivedEvent(mates?.CurrentPartner?.Partner, skills ?? Array.Empty<Skill>().ToList()),
+ ct
+ );
+ }
+}<
\ No newline at end of file
M Core/NosSmooth.Game/PacketHandlers/Skills/PlayerSkillResponder.cs => Core/NosSmooth.Game/PacketHandlers/Skills/PlayerSkillResponder.cs +2 -2
@@ 14,7 14,7 @@ using NosSmooth.Game.Events.Core;
using NosSmooth.Packets.Server.Skills;
using Remora.Results;
-namespace NosSmooth.Game.PacketHandlers.Characters;
+namespace NosSmooth.Game.PacketHandlers.Skills;
/// <summary>
/// Responds to SkiPacket to add skill to the character.
@@ 94,7 94,7 @@ public class PlayerSkillResponder : IPacketResponder<SkiPacket>
otherSkillsFromCharacter.Add(await CreateSkill(newSkill, default));
}
- skills = new Skills(primarySkill, secondarySkill, otherSkillsFromCharacter);
+ skills = new Data.Characters.Skills(primarySkill, secondarySkill, otherSkillsFromCharacter);
await _game.CreateOrUpdateSkillsAsync
(
A Packets/NosSmooth.PacketSerializer.Abstractions/NullableWrapper.cs => Packets/NosSmooth.PacketSerializer.Abstractions/NullableWrapper.cs +31 -0
@@ 0,0 1,31 @@
+//
+// NullableWrapper.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;
+
+namespace NosSmooth.PacketSerializer.Abstractions;
+
+/// <summary>
+/// Wraps a compound value that may not be present
+/// and there will be "-1" instead in the packet.
+/// The converter of underlying type will be called
+/// if and only if the value is not null.
+/// </summary>
+/// <param name="Value">The value.</param>
+/// <typeparam name="T">The underlying type.</typeparam>
+[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1313:Parameter names should begin with lower-case letter", Justification = "Fix this, this should not happen.")]
+public record NullableWrapper<T>(T? Value)
+{
+ /// <summary>
+ /// Unwrap the underlying value.
+ /// </summary>
+ /// <param name="wrapper">The wrapper to unwrap.</param>
+ /// <returns>The unwrapped value.</returns>
+ public static implicit operator T?(NullableWrapper<T> wrapper)
+ {
+ return wrapper.Value;
+ }
+}<
\ No newline at end of file
A Packets/NosSmooth.PacketSerializer/Converters/Common/NullableWrapperConverter.cs => Packets/NosSmooth.PacketSerializer/Converters/Common/NullableWrapperConverter.cs +79 -0
@@ 0,0 1,79 @@
+//
+// NullableWrapperConverter.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;
+using NosSmooth.PacketSerializer.Abstractions;
+using Remora.Results;
+
+namespace NosSmooth.PacketSerializer.Converters.Common;
+
+/// <summary>
+/// Converter of <see cref="NullableWrapper{T}"/>.
+/// </summary>
+/// <typeparam name="T">The underlying type.</typeparam>
+public class NullableWrapperConverter<T> : BaseStringConverter<NullableWrapper<T>>
+{
+ private readonly IStringConverterRepository _converterRepository;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NullableWrapperConverter{T}"/> class.
+ /// </summary>
+ /// <param name="converterRepository">The converter repository.</param>
+ public NullableWrapperConverter(IStringConverterRepository converterRepository)
+ {
+ _converterRepository = converterRepository;
+ }
+
+ /// <inheritdoc />
+ public override Result Serialize(NullableWrapper<T>? obj, PacketStringBuilder builder)
+ {
+ if (obj is null || obj.Value is null)
+ {
+ builder.Append("-1");
+ }
+ else
+ {
+ var converterResult = _converterRepository.GetTypeConverter<T>();
+ if (!converterResult.IsDefined(out var converter))
+ {
+ return Result.FromError(converterResult);
+ }
+
+ return converter.Serialize(obj.Value, builder);
+ }
+
+ return Result.FromSuccess();
+ }
+
+ /// <inheritdoc />
+ public override Result<NullableWrapper<T>?> Deserialize(ref PacketStringEnumerator stringEnumerator)
+ {
+ var tokenResult = stringEnumerator.GetNextToken(out var packetToken, false);
+ if (!tokenResult.IsSuccess)
+ {
+ return Result<NullableWrapper<T>?>.FromError(tokenResult);
+ }
+
+ if (packetToken.Token.Length == 2 && packetToken.Token.StartsWith("-1"))
+ {
+ return Result<NullableWrapper<T>?>.FromSuccess(new NullableWrapper<T>(default));
+ }
+
+ var converterResult = _converterRepository.GetTypeConverter<T>();
+ if (!converterResult.IsDefined(out var converter))
+ {
+ return Result<NullableWrapper<T>?>.FromError(converterResult);
+ }
+
+ var deserializationResult = converter.Deserialize(ref stringEnumerator);
+ if (!deserializationResult.IsDefined(out var deserialization))
+ {
+ return Result<NullableWrapper<T>?>.FromError(deserializationResult);
+ }
+
+ return new NullableWrapper<T>(deserialization);
+ }
+}<
\ No newline at end of file
A Packets/NosSmooth.PacketSerializer/Converters/Common/NullableWrapperConverterFactory.cs => Packets/NosSmooth.PacketSerializer/Converters/Common/NullableWrapperConverterFactory.cs +57 -0
@@ 0,0 1,57 @@
+//
+// NullableWrapperConverterFactory.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;
+using Microsoft.Extensions.DependencyInjection;
+using NosSmooth.PacketSerializer.Abstractions;
+using NosSmooth.PacketSerializer.Converters.Special;
+using NosSmooth.PacketSerializer.Converters.Special.Converters;
+using NosSmooth.PacketSerializer.Extensions;
+using Remora.Results;
+
+namespace NosSmooth.PacketSerializer.Converters.Common;
+
+/// <summary>
+/// Converts <see cref="NullableWrapper{T}"/>.
+/// </summary>
+public class NullableWrapperConverterFactory : IStringConverterFactory
+{
+ private readonly IServiceProvider _serviceProvider;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NullableWrapperConverterFactory"/> class.
+ /// </summary>
+ /// <param name="serviceProvider">The service provider.</param>
+ public NullableWrapperConverterFactory(IServiceProvider serviceProvider)
+ {
+ _serviceProvider = serviceProvider;
+ }
+
+ /// <inheritdoc />
+ public bool ShouldHandle(Type type)
+ => type.GetGenericTypeDefinition() == typeof(NullableWrapper<>);
+
+ /// <inheritdoc />
+ public Result<IStringConverter> CreateTypeSerializer(Type type)
+ {
+ var underlyingType = type.GetGenericArguments()[0];
+ var serializerType = typeof(NullableWrapperConverter<>).MakeGenericType(underlyingType);
+
+ try
+ {
+ return Result<IStringConverter>.FromSuccess
+ ((IStringConverter)ActivatorUtilities.CreateInstance(_serviceProvider, serializerType));
+ }
+ catch (Exception e)
+ {
+ return e;
+ }
+ }
+
+ /// <inheritdoc />
+ public Result<IStringConverter<T>> CreateTypeSerializer<T>()
+ => CreateTypeSerializer(typeof(T)).Cast<IStringConverter<T>, IStringConverter>();
+}<
\ No newline at end of file
M Packets/NosSmooth.PacketSerializer/Extensions/ServiceCollectionExtensions.cs => Packets/NosSmooth.PacketSerializer/Extensions/ServiceCollectionExtensions.cs +1 -0
@@ 77,6 77,7 @@ public static class ServiceCollectionExtensions
.AddStringConverterFactory<ListStringConverterFactory>()
.AddStringConverterFactory<NullableStringConverterFactory>()
.AddStringConverterFactory<EnumStringConverterFactory>()
+ .AddStringConverterFactory<NullableWrapperConverterFactory>()
.AddStringConverter<IntStringConverter>()
.AddStringConverter<BoolStringConverter>()
.AddStringConverter<UIntStringConverter>()
M Packets/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/BasicInlineConverterGenerator.cs => Packets/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/BasicInlineConverterGenerator.cs +1 -1
@@ 51,7 51,7 @@ public class BasicInlineConverterGenerator : IInlineConverterGenerator
{
var type = typeSyntax is not null
? typeSyntax.ToString().TrimEnd('?')
- : typeSymbol?.ToString();
+ : typeSymbol?.ToString().TrimEnd('?');
if (type is null)
{
throw new Exception("TypeSyntax or TypeSymbol has to be non null.");
M Packets/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/ListInlineConverterGenerator.cs => Packets/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/ListInlineConverterGenerator.cs +1 -1
@@ 96,7 96,7 @@ public class ListInlineConverterGenerator : IInlineConverterGenerator
private string GetMethodName(ITypeSymbol genericArgumentType)
{
return
- $"ParseList{genericArgumentType.ToString().Replace('.', '_')}{((genericArgumentType.IsNullable() ?? false) ? "Nullable" : string.Empty)}";
+ $"ParseList{genericArgumentType.ToString().TrimEnd('?').Replace('.', '_').Replace('<', '_').Replace('>', '_')}{((genericArgumentType.IsNullable() ?? false) ? "Nullable" : string.Empty)}";
}
/// <inheritdoc />
M Packets/NosSmooth.PacketSerializersGenerator/SourceGenerator.cs => Packets/NosSmooth.PacketSerializersGenerator/SourceGenerator.cs +6 -0
@@ 130,6 130,12 @@ public class SourceGenerator : ISourceGenerator
$"{packetRecord.GetPrefix()}.{packetRecord.Identifier.NormalizeWhitespace().ToFullString()}Converter.g.cs",
stringWriter.GetStringBuilder().ToString()
);
+
+ File.WriteAllText
+ (
+ Path.Combine(Path.GetTempPath(), $"{packetRecord.GetPrefix()}.{packetRecord.Identifier.NormalizeWhitespace().ToFullString()}Converter.g.cs"),
+ stringWriter.GetStringBuilder().ToString()
+ );
}
}
A Packets/NosSmooth.Packets/Client/Battle/UsePetSkillPacket.cs => Packets/NosSmooth.Packets/Client/Battle/UsePetSkillPacket.cs +37 -0
@@ 0,0 1,37 @@
+//
+// UsePetSkillPacket.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.Battle;
+
+/// <summary>
+/// Sent to use a pet skill.
+/// </summary>
+/// <param name="MateTransportId">The pet skill id.</param>
+/// <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>
+[PacketHeader("u_pet", PacketSource.Client)]
+[GenerateSerializer(true)]
+public record UsePetSkillPacket
+(
+ [PacketIndex(0)]
+ long MateTransportId,
+ [PacketIndex(1)]
+ EntityType TargetEntityType,
+ [PacketIndex(2)]
+ long TargetId,
+ [PacketIndex(3)]
+ byte Unknown,
+ [PacketIndex(4)]
+ byte Unknown1,
+ [PacketIndex(5)]
+ byte Unknown2
+) : IPacket;<
\ No newline at end of file
M Packets/NosSmooth.Packets/Server/Entities/EffectsSubPacket.cs => Packets/NosSmooth.Packets/Server/Entities/EffectsSubPacket.cs +1 -1
@@ 18,7 18,7 @@ namespace NosSmooth.Packets.Server.Entities;
public record EffectsSubPacket
(
[PacketIndex(0)]
- long CardId,
+ short CardId,
[PacketIndex(1, IsOptional = true)]
short? Level
) : IPacket;=
\ No newline at end of file
M Packets/NosSmooth.Packets/Server/Entities/RevivePacket.cs => Packets/NosSmooth.Packets/Server/Entities/RevivePacket.cs +7 -0
@@ 9,6 9,13 @@ using NosSmooth.PacketSerializer.Abstractions.Attributes;
namespace NosSmooth.Packets.Server.Entities;
+/// <summary>
+/// A packet specifying a revival
+/// of an entity, usually a player.
+/// </summary>
+/// <param name="EntityType">The type of the revived entity.</param>
+/// <param name="EntityId">The id of the revived entity.</param>
+/// <param name="TimeSpaceLives">Unknown function, seems like representing lives in a timespace.</param>
[PacketHeader("revive", PacketSource.Server)]
[GenerateSerializer(true)]
public record RevivePacket
M Packets/NosSmooth.Packets/Server/Groups/PinitMateSubPacket.cs => Packets/NosSmooth.Packets/Server/Groups/PinitMateSubPacket.cs +11 -0
@@ 11,6 11,17 @@ using NosSmooth.PacketSerializer.Abstractions.Common;
namespace NosSmooth.Packets.Server.Groups;
+/// <summary>
+/// A sub packet of <see cref="PinitPacket"/>
+/// representing a mate.
+/// </summary>
+/// <param name="MateType">The type of the mate.</param>
+/// <param name="Level">The level of the mate.</param>
+/// <param name="Name">The name of the mate.</param>
+/// <param name="Unknown">Unknown TODO.</param>
+/// <param name="VNum">The VNum of the mate entity.</param>
+[PacketHeader(null, PacketSource.Server)]
+[GenerateSerializer(true)]
public record PinitMateSubPacket
(
[PacketIndex(0)]
M Packets/NosSmooth.Packets/Server/Groups/PinitPlayerSubPacket.cs => Packets/NosSmooth.Packets/Server/Groups/PinitPlayerSubPacket.cs +12 -0
@@ 12,6 12,18 @@ using NosSmooth.PacketSerializer.Abstractions.Common;
namespace NosSmooth.Packets.Server.Groups;
+/// <summary>
+/// A sub packet of <see cref="PinitPacket"/>
+/// representing a player.
+/// </summary>
+/// <param name="GroupPosition">The position in the group.</param>
+/// <param name="Level">The level of the player.</param>
+/// <param name="Name">The name of the player.</param>
+/// <param name="GroupId">The group id of the group character is in.</param>
+/// <param name="Sex">The sex of the player.</param>
+/// <param name="Class">The class of the player.</param>
+/// <param name="MorphVNum">The morph of the player</param>
+/// <param name="HeroLevel">The hero level of the player.</param>
[PacketHeader(null, PacketSource.Server)]
[GenerateSerializer(true)]
public record PinitPlayerSubPacket
M Packets/NosSmooth.Packets/Server/Groups/PstPacket.cs => Packets/NosSmooth.Packets/Server/Groups/PstPacket.cs +3 -1
@@ 16,7 16,9 @@ namespace NosSmooth.Packets.Server.Groups;
/// Party status packet.
/// </summary>
/// <param name="EntityType">The type of the entity.</param>
-/// <param name="GroupPosition">The position in the group of the entity</param>
+/// <param name="EntityId">The id of the entity.</param>
+/// <param name="GroupPosition">The position in the group of a player. Present only for players.</param>
+/// <param name="MateType">The type of a mate. Present only for mates.</param>
/// <param name="HpPercentage">The hp percentage of the entity.</param>
/// <param name="MpPercentage">The mp percentage of the entity.</param>
/// <param name="Hp">The hp of the entity.</param>
M Packets/NosSmooth.Packets/Server/Login/CListPacket.cs => Packets/NosSmooth.Packets/Server/Login/CListPacket.cs +2 -1
@@ 6,6 6,7 @@
using NosSmooth.Packets.Enums.Players;
using NosSmooth.Packets.Server.Maps;
+using NosSmooth.PacketSerializer.Abstractions;
using NosSmooth.PacketSerializer.Abstractions.Attributes;
using NosSmooth.PacketSerializer.Abstractions.Common;
@@ 73,7 74,7 @@ public record CListPacket
[PacketIndex(14)]
byte Unknown3,
[PacketListIndex(15, ListSeparator = '.', InnerSeparator = '.')]
- IReadOnlyList<CListPetSubPacket> PetsSubPacket,
+ IReadOnlyList<NullableWrapper<CListPetSubPacket>> PetsSubPacket,
[PacketIndex(16)]
byte HatDesign,
[PacketIndex(17)]
M Packets/NosSmooth.Packets/Server/Mates/ScNEquipmentSubPacket.cs => Packets/NosSmooth.Packets/Server/Mates/ScNEquipmentSubPacket.cs +5 -5
@@ 21,9 21,9 @@ namespace NosSmooth.Packets.Server.Mates;
public record ScNEquipmentSubPacket
(
[PacketIndex(0)]
- long? ItemVNum,
- [PacketIndex(1, IsOptional = true)]
- long? ItemRare,
- [PacketIndex(2, IsOptional = true)]
- long? ItemUpgrade
+ int ItemVNum,
+ [PacketIndex(1)]
+ sbyte? ItemRare,
+ [PacketIndex(2)]
+ byte ItemUpgrade
) : IPacket;=
\ No newline at end of file
M Packets/NosSmooth.Packets/Server/Mates/ScNPacket.cs => Packets/NosSmooth.Packets/Server/Mates/ScNPacket.cs +10 -9
@@ 7,6 7,7 @@
using NosSmooth.Packets.Enums;
using NosSmooth.Packets.Server.Character;
using NosSmooth.Packets.Server.Maps;
+using NosSmooth.PacketSerializer.Abstractions;
using NosSmooth.PacketSerializer.Abstractions.Attributes;
using NosSmooth.PacketSerializer.Abstractions.Common;
@@ 23,7 24,7 @@ namespace NosSmooth.Packets.Server.Mates;
/// <param name="Loyalty">The loyalty of the partner.</param>
/// <param name="Experience">The experience of the partner.</param>
/// <param name="WeaponSubPacket">Information about partner's weapon.</param>
-/// <param name="ArmodSubPacket">Information about partner's armor.</param>
+/// <param name="ArmorSubPacket">Information about partner's armor.</param>
/// <param name="GauntletSubPacket">Information about partner's gauntlet.</param>
/// <param name="BootsSubPacket">Information about partner's boots.</param>
/// <param name="Unknown1">Unknown TODO.</param>
@@ 73,13 74,13 @@ public record ScNPacket
[PacketIndex(5)]
long Experience,
[PacketIndex(6, InnerSeparator = '.')]
- ScNEquipmentSubPacket? WeaponSubPacket,
+ NullableWrapper<ScNEquipmentSubPacket> WeaponSubPacket,
[PacketIndex(7, InnerSeparator = '.')]
- ScNEquipmentSubPacket? ArmodSubPacket,
+ NullableWrapper<ScNEquipmentSubPacket> ArmorSubPacket,
[PacketIndex(8, InnerSeparator = '.')]
- ScNEquipmentSubPacket? GauntletSubPacket,
+ NullableWrapper<ScNEquipmentSubPacket> GauntletSubPacket,
[PacketIndex(9, InnerSeparator = '.')]
- ScNEquipmentSubPacket? BootsSubPacket,
+ NullableWrapper<ScNEquipmentSubPacket> BootsSubPacket,
[PacketIndex(10, InnerSeparator = '.')]
short Unknown1,
[PacketIndex(11)]
@@ 133,11 134,11 @@ public record ScNPacket
[PacketIndex(35)]
bool IsSummonable,
[PacketIndex(36, InnerSeparator = '.')]
- ScNSpSubPacket? SpSubPacket,
+ NullableWrapper<ScNSpSubPacket> SpSubPacket,
[PacketIndex(37, InnerSeparator = '.')]
- ScNSkillSubPacket? Skill1SubPacket,
+ NullableWrapper<ScNSkillSubPacket> Skill1SubPacket,
[PacketIndex(38, InnerSeparator = '.')]
- ScNSkillSubPacket? Skill2SubPacket,
+ NullableWrapper<ScNSkillSubPacket> Skill2SubPacket,
[PacketIndex(39, InnerSeparator = '.')]
- ScNSkillSubPacket? Skill3SubPacket
+ NullableWrapper<ScNSkillSubPacket> Skill3SubPacket
) : IPacket;=
\ No newline at end of file
M Packets/NosSmooth.Packets/Server/Mates/ScNSkillSubPacket.cs => Packets/NosSmooth.Packets/Server/Mates/ScNSkillSubPacket.cs +3 -3
@@ 21,7 21,7 @@ namespace NosSmooth.Packets.Server.Mates;
public record ScNSkillSubPacket
(
[PacketIndex(0)]
- long? SkillVNum,
- [PacketIndex(1, IsOptional = true)]
- PartnerSkillRank? Rank
+ int SkillVNum,
+ [PacketIndex(1)]
+ PartnerSkillRank Rank
) : IPacket;=
\ No newline at end of file
M Packets/NosSmooth.Packets/Server/Mates/ScNSpSubPacket.cs => Packets/NosSmooth.Packets/Server/Mates/ScNSpSubPacket.cs +3 -3
@@ 20,7 20,7 @@ namespace NosSmooth.Packets.Server.Mates;
public record ScNSpSubPacket
(
[PacketIndex(0)]
- long? ItemVNum,
- [PacketIndex(1, IsOptional = true)]
- byte? AgilityPercentage
+ long ItemVNum,
+ [PacketIndex(1)]
+ byte AgilityPercentage
);=
\ No newline at end of file
M Packets/NosSmooth.Packets/Server/Relations/FInfoPacket.cs => Packets/NosSmooth.Packets/Server/Relations/FInfoPacket.cs +4 -9
@@ 12,16 12,11 @@ namespace NosSmooth.Packets.Server.Relations;
/// <summary>
/// Information update of friend of a character.
/// </summary>
-/// <param name="PlayerId">The id of the friend.</param>
-/// <param name="IsConnected">Whether the friend is connected.</param>
-/// <param name="Name">The name of the friend.</param>[PacketHeader("finfo", PacketSource.Server)]
+/// <param name="FriendSubPackets"></param>
+[PacketHeader("finfo", PacketSource.Server)]
[GenerateSerializer(true)]
public record FInfoPacket
(
- [PacketIndex(0)]
- long PlayerId,
- [PacketIndex(1)]
- bool IsConnected,
- [PacketIndex(2)]
- NameString Name
+ [PacketListIndex(0, InnerSeparator = '.', ListSeparator = ' ')]
+ IReadOnlyList<FInfoSubPacket> FriendSubPackets
) : IPacket;=
\ No newline at end of file
A Packets/NosSmooth.Packets/Server/Relations/FInfoSubPacket.cs => Packets/NosSmooth.Packets/Server/Relations/FInfoSubPacket.cs +29 -0
@@ 0,0 1,29 @@
+//
+// FInfoSubPacket.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;
+using NosSmooth.PacketSerializer.Abstractions.Common;
+
+namespace NosSmooth.Packets.Server.Relations;
+
+/// <summary>
+/// A sub packet of <see cref="FInfoPacket"/>
+/// containing information about a friend.
+/// </summary>
+/// <param name="PlayerId">The id of the player.</param>
+/// <param name="IsConnected">Whether the player is connected.</param>
+/// <param name="Name">The name of the player.</param>
+[PacketHeader(null, PacketSource.Server)]
+[GenerateSerializer(true)]
+public record FInfoSubPacket
+(
+ [PacketIndex(0)]
+ long PlayerId,
+ [PacketIndex(1)]
+ bool IsConnected,
+ [PacketIndex(2)]
+ NameString Name
+);<
\ No newline at end of file
A Samples/FileClient/Responders/InventoryInitializedResponder.cs => Samples/FileClient/Responders/InventoryInitializedResponder.cs +52 -0
@@ 0,0 1,52 @@
+//
+// InventoryInitializedResponder.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.Enums;
+using NosSmooth.Data.Abstractions.Language;
+using NosSmooth.Game.Data.Items;
+using NosSmooth.Game.Events.Core;
+using NosSmooth.Game.Events.Inventory;
+using NosSmooth.Game.Extensions;
+using Remora.Results;
+
+namespace FileClient.Responders;
+
+/// <inheritdoc />
+public class InventoryInitializedResponder : IGameResponder<InventoryInitializedEvent>
+{
+ private readonly ILanguageService _languageService;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="InventoryInitializedResponder"/> class.
+ /// </summary>
+ /// <param name="languageService">The langauge service.</param>
+ public InventoryInitializedResponder(ILanguageService languageService)
+ {
+ _languageService = languageService;
+
+ }
+
+ /// <inheritdoc />
+ public async Task<Result> Respond(InventoryInitializedEvent gameEvent, CancellationToken ct = default)
+ {
+ foreach (var bag in gameEvent.Inventory)
+ {
+ foreach (var slot in bag)
+ {
+ var item = slot.Item;
+ if (item?.Info is not null && bag.Type != item.Info.BagType)
+ {
+ var translatedResult = await _languageService.GetTranslationAsync(item.Info.Name, Language.Cz, ct);
+ var entity = translatedResult.Entity;
+
+ Console.WriteLine(entity + $", {item.ItemVNum} is: {bag.Type}, should be: {item.Info.BagType.Convert()}");
+ }
+ }
+ }
+
+ return Result.FromSuccess();
+ }
+}<
\ No newline at end of file