//
// 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;
///
/// A mates initialization responder.
///
public class MatesInitResponder : IPacketResponder, IPacketResponder,
IPacketResponder, IPacketResponder, IPacketResponder
{
private readonly Game _game;
private readonly IInfoService _infoService;
private readonly EventDispatcher _eventDispatcher;
private readonly ILogger _logger;
///
/// Initializes a new instance of the class.
///
/// The game.
/// The info service.
/// The event dispatcher.
/// The logger.
public MatesInitResponder
(
Game game,
IInfoService infoService,
EventDispatcher eventDispatcher,
ILogger logger
)
{
_game = game;
_infoService = infoService;
_eventDispatcher = eventDispatcher;
_logger = logger;
}
///
public async Task Respond(PacketEventArgs 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
);
}
///
public async Task Respond(PacketEventArgs 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
);
}
///
public async Task Respond(PacketEventArgs 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.TransportId == partner.EntityId);
if (gamePartner is not null && gamePartner != m.CurrentPartner?.Partner)
{
m.CurrentPartner = new PartyPartner(gamePartner);
}
}
else
{
m.CurrentPartner = null;
}
if (pet is not null)
{
gamePet = _game.Mates?.Pets.FirstOrDefault(x => x.TransportId == pet.EntityId);
if (gamePet is not null && gamePet != m.CurrentPet?.Pet)
{
m.CurrentPet = new PartyPet(gamePet);
}
}
else
{
m.CurrentPet = null;
}
return m;
},
ct: ct
);
return await _eventDispatcher.DispatchEvent
(
new MatesPartyInitializedEvent(gamePet, gamePartner),
ct
);
}
///
public async Task Respond(PacketEventArgs 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();
}
///
public async Task Respond(PacketEventArgs packetArgs, CancellationToken ct = default)
{
await _game.CreateOrUpdateMatesAsync
(
() => null,
m =>
{
m.Clear();
return m;
},
ct: ct
);
return Result.FromSuccess();
}
private async Task 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 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);
}
}