From e298e76e0f132398c7640b6c6e0b789070edac08 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Fri, 18 Feb 2022 23:32:40 +0100 Subject: [PATCH] feat(combat): add simple attack combat technique --- .../Techniques/ICombatTechnique.cs | 26 ++++ .../Techniques/SimpleAttackTechnique.cs | 147 ++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 Extensions/NosSmooth.Extensions.Combat/Techniques/ICombatTechnique.cs create mode 100644 Extensions/NosSmooth.Extensions.Combat/Techniques/SimpleAttackTechnique.cs diff --git a/Extensions/NosSmooth.Extensions.Combat/Techniques/ICombatTechnique.cs b/Extensions/NosSmooth.Extensions.Combat/Techniques/ICombatTechnique.cs new file mode 100644 index 0000000..a7732de --- /dev/null +++ b/Extensions/NosSmooth.Extensions.Combat/Techniques/ICombatTechnique.cs @@ -0,0 +1,26 @@ +// +// ICombatTechnique.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.Extensions.Combat.Operations; +using OneOf.Types; +using Remora.Results; + +namespace NosSmooth.Extensions.Combat.Techniques; + +/// +/// A combat technique that allows to handle the whole combat situations using step callbacks. +/// +/// +/// The callback methods decide the next steps, used in . +/// +public interface ICombatTechnique +{ + public bool ShouldContinue(ICombatState state); + + public Result HandleCombatStep(ICombatState state); + + public Result HandleError(ICombatState state, ICombatOperation operation, Result result); +} \ No newline at end of file diff --git a/Extensions/NosSmooth.Extensions.Combat/Techniques/SimpleAttackTechnique.cs b/Extensions/NosSmooth.Extensions.Combat/Techniques/SimpleAttackTechnique.cs new file mode 100644 index 0000000..c0b273f --- /dev/null +++ b/Extensions/NosSmooth.Extensions.Combat/Techniques/SimpleAttackTechnique.cs @@ -0,0 +1,147 @@ +// +// SimpleAttackTechnique.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.Extensions.Combat.Errors; +using NosSmooth.Extensions.Combat.Extensions; +using NosSmooth.Extensions.Combat.Operations; +using NosSmooth.Extensions.Combat.Selectors; +using NosSmooth.Extensions.Pathfinding; +using NosSmooth.Game.Data.Characters; +using NosSmooth.Game.Data.Entities; +using Remora.Results; + +namespace NosSmooth.Extensions.Combat.Techniques; + +/// +/// A combat technique that will attack on the specified enemy. +/// +public class SimpleAttackTechnique : ICombatTechnique +{ + private readonly int _targetId; + private readonly WalkManager _walkManager; + private readonly ISkillSelector _skillSelector; + private readonly IItemSelector _itemSelector; + + private Skill? _currentSkill; + private ILivingEntity? _target; + + /// + /// + /// + /// + /// + /// + /// + public SimpleAttackTechnique + ( + int targetId, + WalkManager walkManager, + ISkillSelector skillSelector, + IItemSelector itemSelector + ) + { + _targetId = targetId; + _walkManager = walkManager; + _skillSelector = skillSelector; + _itemSelector = itemSelector; + } + + /// + public bool ShouldContinue(ICombatState state) + { + var map = state.Game.CurrentMap; + if (map is null) + { + return false; + } + + var entity = map.Entities.GetEntity(_targetId); + return !(entity is null || entity.Hp is not null && (entity.Hp.Amount <= 0 || entity.Hp.Percentage <= 0)); + } + + /// + public Result HandleCombatStep(ICombatState state) + { + var map = state.Game.CurrentMap; + if (map is null) + { + return new MapNotInitializedError(); + } + + if (_target is null) + { + var entity = map.Entities.GetEntity(_targetId); + if (entity is null) + { + return new EntityNotFoundError(); + } + + _target = entity; + } + + var character = state.Game.Character; + if (character is null) + { + return new CharacterNotInitializedError(); + } + + if (_currentSkill is null) + { + var skills = character.Skills; + if (skills is null) + { + return new CharacterNotInitializedError("Skills"); + } + + var characterMp = character.Mp?.Amount ?? 0; + var usableSkills = new[] { skills.PrimarySkill, skills.SecondarySkill } + .Concat(skills.OtherSkills) + .Where(x => !x.IsOnCooldown && characterMp >= (x.Info?.MpCost ?? long.MaxValue)); + + var skillResult = _skillSelector.GetSelectedSkill(usableSkills); + if (!skillResult.IsSuccess) + { + if (skillResult.Error is SkillNotFoundError) + { + return Result.FromSuccess(); + } + + return Result.FromError(skillResult); + } + + _currentSkill = skillResult.Entity; + } + + if (_currentSkill.Info is null) + { + var currentSkill = _currentSkill; + _currentSkill = null; + return new MissingInfoError("skill", currentSkill.SkillVNum); + } + + if (character.Position is null || _target.Position is null) + { + return new; + } + + if (!character.Position.Value.IsInRange(_target.Position.Value, _currentSkill.Info.Range)) + { + state.WalkInRange(_walkManager, _target, _currentSkill.Info.Range); + } + else + { + state.UseSkill(_currentSkill, _target); + } + + return Result.FromSuccess(); + } + + /// + public Result HandleError(ICombatState state, ICombatOperation operation, Result result) + { + return result; + } +} \ No newline at end of file -- 2.49.0