~ruther/NosSmooth

e298e76e0f132398c7640b6c6e0b789070edac08 — Rutherther 3 years ago f0507f7
feat(combat): add simple attack combat technique
A Extensions/NosSmooth.Extensions.Combat/Techniques/ICombatTechnique.cs => Extensions/NosSmooth.Extensions.Combat/Techniques/ICombatTechnique.cs +26 -0
@@ 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;

/// <summary>
/// A combat technique that allows to handle the whole combat situations using step callbacks.
/// </summary>
/// <remarks>
/// The callback methods decide the next steps, used in <see cref="CombatManager"/>.
/// </remarks>
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

A Extensions/NosSmooth.Extensions.Combat/Techniques/SimpleAttackTechnique.cs => Extensions/NosSmooth.Extensions.Combat/Techniques/SimpleAttackTechnique.cs +147 -0
@@ 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;

/// <summary>
/// A combat technique that will attack on the specified enemy.
/// </summary>
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;

    /// <summary>
    /// 
    /// </summary>
    /// <param name="targetId"></param>
    /// <param name="walkManager"></param>
    /// <param name="skillSelector"></param>
    /// <param name="itemSelector"></param>
    public SimpleAttackTechnique
    (
        int targetId,
        WalkManager walkManager,
        ISkillSelector skillSelector,
        IItemSelector itemSelector
    )
    {
        _targetId = targetId;
        _walkManager = walkManager;
        _skillSelector = skillSelector;
        _itemSelector = itemSelector;
    }

    /// <inheritdoc />
    public bool ShouldContinue(ICombatState state)
    {
        var map = state.Game.CurrentMap;
        if (map is null)
        {
            return false;
        }

        var entity = map.Entities.GetEntity<ILivingEntity>(_targetId);
        return !(entity is null || entity.Hp is not null && (entity.Hp.Amount <= 0 || entity.Hp.Percentage <= 0));
    }

    /// <inheritdoc />
    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<ILivingEntity>(_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();
    }

    /// <inheritdoc />
    public Result HandleError(ICombatState state, ICombatOperation operation, Result result)
    {
        return result;
    }
}
\ No newline at end of file

Do not follow this link