// // SkillUsedResponder.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.Entities; using NosSmooth.Game.Data.Info; using NosSmooth.Game.Events.Battle; using NosSmooth.Game.Events.Characters; using NosSmooth.Game.Events.Core; using NosSmooth.Game.Events.Entities; using NosSmooth.Game.Extensions; using NosSmooth.Game.Helpers; using NosSmooth.Packets.Enums.Entities; using NosSmooth.Packets.Server.Battle; using NosSmooth.Packets.Server.Skills; using Remora.Results; namespace NosSmooth.Game.PacketHandlers.Entities; /// /// Responds to skill used packet. /// public class SkillUsedResponder : IPacketResponder, IPacketResponder { private readonly Game _game; private readonly EventDispatcher _eventDispatcher; private readonly IInfoService _infoService; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The game. /// The event dispatcher. /// The info service. /// The logger. public SkillUsedResponder ( Game game, EventDispatcher eventDispatcher, IInfoService infoService, ILogger logger ) { _game = game; _eventDispatcher = eventDispatcher; _infoService = infoService; _logger = logger; } /// public async Task Respond(PacketEventArgs packetArgs, CancellationToken ct = default) { var packet = packetArgs.Packet; var map = _game.CurrentMap; // TODO: add to map if the entity is created? var caster = map?.Entities?.GetEntity (packet.CasterEntityId) ?? (ILivingEntity)EntityHelpers.CreateEntity (packet.CasterEntityType, packet.CasterEntityId); var target = map?.Entities?.GetEntity (packet.TargetEntityId) ?? (ILivingEntity)EntityHelpers.CreateEntity (packet.TargetEntityType, packet.TargetEntityId); if (target.Hp is null) { target.Hp = new Health { Percentage = packet.HpPercentage }; } else { target.Hp.Percentage = packet.HpPercentage; } Skill? skillEntity; var skills = _game.Skills; if (packet.SkillVNum is not null && caster is Character character && skills is not null) { var skillResult = skills.TryGetSkillByVNum(packet.SkillVNum.Value); if (skillResult.IsDefined(out skillEntity)) { skillEntity.LastUseTime = DateTimeOffset.Now; skillEntity.IsOnCooldown = true; } else { var skillInfoResult = await _infoService.GetSkillInfoAsync(packet.SkillVNum.Value, ct); skillEntity = new Skill (packet.SkillVNum.Value, null, skillInfoResult.IsSuccess ? skillInfoResult.Entity : null); } } else if (packet.SkillVNum is not null) { var skillInfoResult = await _infoService.GetSkillInfoAsync(packet.SkillVNum.Value, ct); if (!skillInfoResult.IsSuccess && packet.SkillVNum != 0) { _logger.LogWarning ( "Could not obtain a skill info for vnum {vnum}: {error}", packet.SkillVNum, skillInfoResult.ToFullString() ); } skillEntity = new Skill (packet.SkillVNum.Value, null, skillInfoResult.IsSuccess ? skillInfoResult.Entity : null); } else { skillEntity = new Skill(-1, null, null); } var dispatchResult = await _eventDispatcher.DispatchEvent ( new SkillUsedEvent ( caster, target, skillEntity, new Position(packet.PositionX, packet.PositionY), packet.HitMode, packet.Damage ), ct ); if (!packet.TargetIsAlive) { if (target is not ILivingEntity living || (living.Type is not(EntityType.Player or EntityType.Npc))) { map?.Entities?.RemoveEntity(target.Id); } target.Hp.Amount = 0; target.Hp.Percentage = 0; var diedResult = await _eventDispatcher.DispatchEvent(new EntityDiedEvent(target, skillEntity), ct); if (!diedResult.IsSuccess) { return dispatchResult.IsSuccess ? diedResult : new AggregateError(diedResult, diedResult); } } return dispatchResult; } /// public async Task Respond(PacketEventArgs packetArgs, CancellationToken ct = default) { var packet = packetArgs.Packet; var skills = _game.Skills; if (skills is not null) { var skillResult = skills.TryGetSkillByCastId(packet.SkillId); if (skillResult.IsDefined(out var skillEntity)) { skillEntity.IsOnCooldown = false; } await _eventDispatcher.DispatchEvent (new SkillReadyEvent(skillEntity, skillEntity?.SkillVNum ?? packet.SkillId), ct); } else { await _eventDispatcher.DispatchEvent(new SkillReadyEvent(null, packet.SkillId), ct); } return Result.FromSuccess(); } }