// // 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.Data.Abstractions.Enums; 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, walk within range and use skill until the enemy is dead. /// public class SimpleAttackTechnique : ICombatTechnique { private readonly long _targetId; private readonly WalkManager _walkManager; private readonly ISkillSelector _skillSelector; private Skill? _currentSkill; private ILivingEntity? _target; /// /// Initializes a new instance of the class. /// /// The target entity id. /// The walk manager. /// The skill selector. public SimpleAttackTechnique ( long targetId, WalkManager walkManager, ISkillSelector skillSelector ) { _targetId = targetId; _walkManager = walkManager; _skillSelector = skillSelector; } /// 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.Info is not null && x.Info.HitType != HitType.AlliesInZone) .Where(x => !x.IsOnCooldown && characterMp >= (x.Info?.MpCost ?? long.MaxValue)); var skillResult = _skillSelector.GetSelectedSkill(usableSkills); if (!skillResult.IsSuccess) { if (skillResult.Error is SkillNotFoundError) { return _target.Id; } 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) { return new CharacterNotInitializedError("Position"); } if (_target.Position is null) { return new EntityNotFoundError(); } 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); } else { state.UseSkill(_currentSkill, _target); _currentSkill = null; } return _target.Id; } /// public Result HandleError(ICombatState state, ICombatOperation operation, Result result) { return result; } }