From c02cf72a8c911f1127991832b4b050250a586ba4 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Sun, 1 Jan 2023 18:40:44 +0100 Subject: [PATCH] feat(combat): support self targeted and area skills --- .../Extensions/CombatStateExtensions.cs | 7 ++- .../Operations/UsePrimarySkillOperation.cs | 14 ++++- .../Operations/UseSkillOperation.cs | 61 ++++++++++++++++--- .../Responders/SkillUseResponder.cs | 17 ++++-- .../Techniques/SimpleAttackTechnique.cs | 10 ++- 5 files changed, 91 insertions(+), 18 deletions(-) diff --git a/Extensions/NosSmooth.Extensions.Combat/Extensions/CombatStateExtensions.cs b/Extensions/NosSmooth.Extensions.Combat/Extensions/CombatStateExtensions.cs index 829d5a6..5a8ae5c 100644 --- a/Extensions/NosSmooth.Extensions.Combat/Extensions/CombatStateExtensions.cs +++ b/Extensions/NosSmooth.Extensions.Combat/Extensions/CombatStateExtensions.cs @@ -65,7 +65,12 @@ public static class CombatStateExtensions /// The target to use skill at. public static void UseSkill(this ICombatState combatState, Skill skill, ILivingEntity target) { - combatState.EnqueueOperation(new UseSkillOperation(skill, target)); + if (combatState.Game.Character is null) + { + throw new InvalidOperationException("The character is not initialized."); + } + + combatState.EnqueueOperation(new UseSkillOperation(skill, combatState.Game.Character, target)); } /// diff --git a/Extensions/NosSmooth.Extensions.Combat/Operations/UsePrimarySkillOperation.cs b/Extensions/NosSmooth.Extensions.Combat/Operations/UsePrimarySkillOperation.cs index b302a8e..3541006 100644 --- a/Extensions/NosSmooth.Extensions.Combat/Operations/UsePrimarySkillOperation.cs +++ b/Extensions/NosSmooth.Extensions.Combat/Operations/UsePrimarySkillOperation.cs @@ -30,7 +30,12 @@ public record UsePrimarySkillOperation(ILivingEntity Target) : ICombatOperation return Result.FromError(primarySkillResult); } - _useSkillOperation = new UseSkillOperation(primarySkill, Target); + if (combatState.Game.Character is null) + { + return new CharacterNotInitializedError(); + } + + _useSkillOperation = new UseSkillOperation(primarySkill, combatState.Game.Character, Target); } return _useSkillOperation.CanBeUsed(combatState); @@ -47,7 +52,12 @@ public record UsePrimarySkillOperation(ILivingEntity Target) : ICombatOperation return Result.FromError(primarySkillResult); } - _useSkillOperation = new UseSkillOperation(primarySkill, Target); + if (combatState.Game.Character is null) + { + return new CharacterNotInitializedError(); + } + + _useSkillOperation = new UseSkillOperation(primarySkill, combatState.Game.Character, Target); } return await _useSkillOperation.UseAsync(combatState, ct); diff --git a/Extensions/NosSmooth.Extensions.Combat/Operations/UseSkillOperation.cs b/Extensions/NosSmooth.Extensions.Combat/Operations/UseSkillOperation.cs index 7e61073..897364b 100644 --- a/Extensions/NosSmooth.Extensions.Combat/Operations/UseSkillOperation.cs +++ b/Extensions/NosSmooth.Extensions.Combat/Operations/UseSkillOperation.cs @@ -4,10 +4,14 @@ // 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; using System.Xml.XPath; +using NosSmooth.Data.Abstractions.Enums; +using NosSmooth.Data.Abstractions.Infos; using NosSmooth.Extensions.Combat.Errors; using NosSmooth.Game.Data.Characters; using NosSmooth.Game.Data.Entities; +using NosSmooth.Packets; using NosSmooth.Packets.Client.Battle; using Remora.Results; @@ -17,8 +21,9 @@ namespace NosSmooth.Extensions.Combat.Operations; /// A combat operation to use a skill. /// /// The skill to use. +/// The caster entity that is using the skill. /// The target entity to use the skill at. -public record UseSkillOperation(Skill Skill, ILivingEntity Target) : ICombatOperation +public record UseSkillOperation(Skill Skill, ILivingEntity Caster, ILivingEntity Target) : ICombatOperation { /// public Result CanBeUsed(ICombatState combatState) @@ -48,19 +53,11 @@ public record UseSkillOperation(Skill Skill, ILivingEntity Target) : ICombatOper return new MissingInfoError("skill", Skill.SkillVNum); } - // TODO: support for area skills, support skills that use x, y coordinates (like dashes or teleports) using var linkedSource = CancellationTokenSource.CreateLinkedTokenSource(ct); await combatState.CombatManager.RegisterSkillCancellationTokenAsync(linkedSource, ct); var sendResponse = await combatState.Client.SendPacketAsync ( - new UseSkillPacket - ( - Skill.Info.CastId, - Target.Type, - Target.Id, - null, - null - ), + CreateSkillUsePacket(Skill.Info), ct ); @@ -84,4 +81,48 @@ public record UseSkillOperation(Skill Skill, ILivingEntity Target) : ICombatOper return Result.FromSuccess(); } + + private IPacket CreateSkillUsePacket(ISkillInfo info) + { + switch (info.TargetType) + { + case TargetType.SelfOrTarget: // a buff? + case TargetType.Self: + return CreateSelfTargetedSkillPacket(info); + case TargetType.NoTarget: // area skill? + return CreateAreaSkillPacket(info); + case TargetType.Target: + return CreateTargetedSkillPacket(info); + } + + throw new UnreachableException(); + } + + private IPacket CreateAreaSkillPacket(ISkillInfo info) + => new UseAOESkillPacket + ( + info.CastId, + Target.Position!.Value.X, + Target.Position.Value.Y + ); + + private IPacket CreateTargetedSkillPacket(ISkillInfo info) + => new UseSkillPacket + ( + info.CastId, + Target.Type, + Target.Id, + null, + null + ); + + private IPacket CreateSelfTargetedSkillPacket(ISkillInfo info) + => new UseSkillPacket + ( + info.CastId, + Caster.Type, + Caster.Id, + null, + null + ); } \ No newline at end of file diff --git a/Extensions/NosSmooth.Extensions.Combat/Responders/SkillUseResponder.cs b/Extensions/NosSmooth.Extensions.Combat/Responders/SkillUseResponder.cs index 796654a..f24c927 100644 --- a/Extensions/NosSmooth.Extensions.Combat/Responders/SkillUseResponder.cs +++ b/Extensions/NosSmooth.Extensions.Combat/Responders/SkillUseResponder.cs @@ -1,5 +1,5 @@ // -// SuResponder.cs +// SkillUseResponder.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. @@ -17,27 +17,36 @@ namespace NosSmooth.Extensions.Combat.Responders; public class SuResponder : IPacketResponder, IPacketResponder { private readonly CombatManager _combatManager; + private readonly Game.Game _game; /// /// Initializes a new instance of the class. /// /// The combat manager. - public SuResponder(CombatManager combatManager) + /// The game. + public SuResponder(CombatManager combatManager, Game.Game game) { _combatManager = combatManager; + _game = game; } /// public Task Respond(PacketEventArgs packetArgs, CancellationToken ct = default) { - _combatManager.CancelSkillTokensAsync(ct); + if (packetArgs.Packet.CasterEntityId == _game.Character?.Id) + { + _combatManager.CancelSkillTokensAsync(ct); + } return Task.FromResult(Result.FromSuccess()); } /// public Task Respond(PacketEventArgs packetArgs, CancellationToken ct = default) { - _combatManager.CancelSkillTokensAsync(ct); + if (packetArgs.Packet.CasterEntityId == _game.Character?.Id) + { + _combatManager.CancelSkillTokensAsync(ct); + } return Task.FromResult(Result.FromSuccess()); } } \ No newline at end of file diff --git a/Extensions/NosSmooth.Extensions.Combat/Techniques/SimpleAttackTechnique.cs b/Extensions/NosSmooth.Extensions.Combat/Techniques/SimpleAttackTechnique.cs index de81bea..498ddb8 100644 --- a/Extensions/NosSmooth.Extensions.Combat/Techniques/SimpleAttackTechnique.cs +++ b/Extensions/NosSmooth.Extensions.Combat/Techniques/SimpleAttackTechnique.cs @@ -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.Enums; using NosSmooth.Extensions.Combat.Errors; using NosSmooth.Extensions.Combat.Extensions; using NosSmooth.Extensions.Combat.Operations; @@ -95,6 +96,7 @@ public class SimpleAttackTechnique : ICombatTechnique var characterMp = character.Mp?.Amount ?? 0; var usableSkills = new[] { skills.PrimarySkill, skills.SecondarySkill } .Concat(skills.OtherSkills) + .Where(x => x.Info is not null && x.Info.HitType != HitType.AlliesInZone) .Where(x => !x.IsOnCooldown && characterMp >= (x.Info?.MpCost ?? long.MaxValue)); var skillResult = _skillSelector.GetSelectedSkill(usableSkills); @@ -128,7 +130,13 @@ public class SimpleAttackTechnique : ICombatTechnique return new EntityNotFoundError(); } - if (!character.Position.Value.IsInRange(_target.Position.Value, _currentSkill.Info.Range)) + var range = _currentSkill.Info.Range; + if (_currentSkill.Info.TargetType == TargetType.Self && _currentSkill.Info.HitType == HitType.EnemiesInZone) + { + range = _currentSkill.Info.ZoneRange; + } + + if (!character.Position.Value.IsInRange(_target.Position.Value, range)) { state.WalkInRange(_walkManager, _target, _currentSkill.Info.Range); } -- 2.48.1