~ruther/NosSmooth

2dbb70b37dc4be1b205ed72703767b6c3f796611 — František Boháček 2 years ago 977eadf
feat(combat): add possibility to cancel operation to quit the combat state early
M Extensions/NosSmooth.Extensions.Combat/CombatManager.cs => Extensions/NosSmooth.Extensions.Combat/CombatManager.cs +16 -1
@@ 145,6 145,20 @@ public class CombatManager : IStatefulEntity
            currentOperation = null;
        }

        if (combatState.ShouldQuit)
        {
            if (currentOperation is not null && !currentOperation.IsExecuting())
            {
                currentOperation.Dispose();
                currentOperation = null;
            }

            if (currentOperation is not null && currentOperation.MayBeCancelled)
            {
                currentOperation.Cancel();
            }
        }

        if (currentOperation is null && !combatState.ShouldQuit)
        { // waiting for an operation.
            currentOperation = combatState.NextOperation(queueType);


@@ 181,7 195,8 @@ public class CombatManager : IStatefulEntity
            {
                case CanBeUsedResponse.WontBeUsable:
                case CanBeUsedResponse.MustWait:
                    var waitingResult = technique.HandleWaiting(queueType, combatState, currentOperation, canBeUsedError!);
                    var waitingResult = technique.HandleWaiting
                        (queueType, combatState, currentOperation, canBeUsedError!);

                    if (!waitingResult.IsSuccess)
                    {

M Extensions/NosSmooth.Extensions.Combat/Operations/CompoundOperation.cs => Extensions/NosSmooth.Extensions.Combat/Operations/CompoundOperation.cs +12 -0
@@ 19,6 19,7 @@ public class CompoundOperation : ICombatOperation
    private readonly ICombatTechnique _technique;
    private readonly ICombatOperation[] _operations;
    private readonly OperationQueueType _queueType;
    private ICombatOperation? _currentOperation;
    private CancellationTokenSource? _ct;
    private Task<Result>? _compoundOperation;



@@ 54,6 55,9 @@ public class CompoundOperation : ICombatOperation
    public OperationQueueType QueueType => _queueType;

    /// <inheritdoc />
    public bool MayBeCancelled => _currentOperation?.MayBeCancelled ?? true;

    /// <inheritdoc />
    public Task<Result> BeginExecution(ICombatState combatState, CancellationToken ct = default)
    {
        if (_compoundOperation is not null)


@@ 110,10 114,18 @@ public class CompoundOperation : ICombatOperation
    public Result CanBeUsed(ICombatState combatState)
        => _operations[0].CanBeUsed(combatState);

    /// <inheritdoc />
    public void Cancel()
    {
        _currentOperation?.Cancel();
        _ct?.Cancel();
    }

    private async Task<Result> UseAsync(ICombatState combatState, CancellationToken ct)
    {
        foreach (var operation in _operations)
        {
            _currentOperation = operation;
            CanBeUsedResponse canBeUsed = CanBeUsedResponse.MustWait;

            while (canBeUsed != CanBeUsedResponse.CanBeUsed)

M Extensions/NosSmooth.Extensions.Combat/Operations/ICombatOperation.cs => Extensions/NosSmooth.Extensions.Combat/Operations/ICombatOperation.cs +10 -0
@@ 26,6 26,11 @@ public interface ICombatOperation : IDisposable
    public OperationQueueType QueueType { get; }

    /// <summary>
    /// Gets whether the operation may currently be cancelled (disposed).
    /// </summary>
    public bool MayBeCancelled { get; }

    /// <summary>
    /// Begin the execution without waiting for the finished state.
    /// </summary>
    /// <param name="combatState">The combat state.</param>


@@ 63,4 68,9 @@ public interface ICombatOperation : IDisposable
    /// <param name="combatState">The combat state.</param>
    /// <returns>Whether the operation can be used right away.</returns>
    public Result CanBeUsed(ICombatState combatState);

    /// <summary>
    /// Cancel the current operation.
    /// </summary>
    public void Cancel();
}
\ No newline at end of file

M Extensions/NosSmooth.Extensions.Combat/Operations/UseItemOperation.cs => Extensions/NosSmooth.Extensions.Combat/Operations/UseItemOperation.cs +11 -1
@@ 27,6 27,9 @@ public record UseItemOperation(InventoryItem Item) : ICombatOperation
    public OperationQueueType QueueType => OperationQueueType.Item;

    /// <inheritdoc />
    public bool MayBeCancelled => true;

    /// <inheritdoc />
    public Task<Result> BeginExecution(ICombatState combatState, CancellationToken ct = default)
    {
        if (_useItemOperation is not null)


@@ 50,7 53,7 @@ public record UseItemOperation(InventoryItem Item) : ICombatOperation
            return Result.FromSuccess();
        }

        BeginExecution(combatState, ct);
        await BeginExecution(combatState, ct);
        if (_useItemOperation is null)
        {
            throw new UnreachableException();


@@ 83,9 86,16 @@ public record UseItemOperation(InventoryItem Item) : ICombatOperation
        => Result.FromSuccess();

    /// <inheritdoc />
    public void Cancel()
    {
        _ct?.Cancel();
    }

    /// <inheritdoc />
    public void Dispose()
    {
        _ct?.Cancel();
        _useItemOperation?.Dispose();
        _ct?.Dispose();
    }
}
\ No newline at end of file

M Extensions/NosSmooth.Extensions.Combat/Operations/UseSkillOperation.cs => Extensions/NosSmooth.Extensions.Combat/Operations/UseSkillOperation.cs +9 -0
@@ 40,6 40,9 @@ public record UseSkillOperation
    public OperationQueueType QueueType => OperationQueueType.TotalControl;

    /// <inheritdoc />
    public bool MayBeCancelled => false;

    /// <inheritdoc />
    public async Task<Result> BeginExecution(ICombatState combatState, CancellationToken ct = default)
    {
        if (_contract is not null)


@@ 143,6 146,12 @@ public record UseSkillOperation
        return Result.FromSuccess();
    }

    /// <inheritdoc />
    public void Cancel()
    {
        throw new InvalidOperationException();
    }

    private Result<IContract<SkillUsedEvent, UseSkillStates>> ContractSkill(ISkillInfo info)
    {
        if (info.AttackType == AttackType.Dash)

M Extensions/NosSmooth.Extensions.Combat/Operations/WalkInRangeOperation.cs => Extensions/NosSmooth.Extensions.Combat/Operations/WalkInRangeOperation.cs +10 -0
@@ 36,6 36,9 @@ public record WalkInRangeOperation
    public OperationQueueType QueueType => OperationQueueType.TotalControl;

    /// <inheritdoc />
    public bool MayBeCancelled => true;

    /// <inheritdoc />
    public Task<Result> BeginExecution(ICombatState combatState, CancellationToken ct = default)
    {
        if (_walkInRangeOperation is not null)


@@ 105,6 108,12 @@ public record WalkInRangeOperation
        return Result.FromSuccess();
    }

    /// <inheritdoc />
    public void Cancel()
    {
        _ct?.Cancel();
    }

    private async Task<Result> UseAsync(ICombatState combatState, CancellationToken ct = default)
    {
        var character = combatState.Game.Character;


@@ 201,5 210,6 @@ public record WalkInRangeOperation
    {
        _ct?.Cancel();
        _walkInRangeOperation?.Dispose();
        _ct?.Dispose();
    }
}
\ No newline at end of file

M Extensions/NosSmooth.Extensions.Combat/Operations/WalkOperation.cs => Extensions/NosSmooth.Extensions.Combat/Operations/WalkOperation.cs +10 -0
@@ 26,6 26,9 @@ public record WalkOperation(WalkManager WalkManager, short X, short Y) : ICombat
    public OperationQueueType QueueType => OperationQueueType.TotalControl;

    /// <inheritdoc />
    public bool MayBeCancelled => true;

    /// <inheritdoc />
    public Task<Result> BeginExecution(ICombatState combatState, CancellationToken ct = default)
    {
        if (_walkOperation is not null)


@@ 95,6 98,12 @@ public record WalkOperation(WalkManager WalkManager, short X, short Y) : ICombat
        return Result.FromSuccess();
    }

    /// <inheritdoc />
    public void Cancel()
    {
        _ct?.Cancel();
    }

    private Task<Result> UseAsync(ICombatState combatState, CancellationToken ct = default)
        => WalkManager.GoToAsync(X, Y, true, ct);



@@ 103,5 112,6 @@ public record WalkOperation(WalkManager WalkManager, short X, short Y) : ICombat
    {
        _ct?.Cancel();
        _walkOperation?.Dispose();
        _ct?.Dispose();
    }
}
\ No newline at end of file