From 2dbb70b37dc4be1b205ed72703767b6c3f796611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Boh=C3=A1=C4=8Dek?= Date: Sat, 21 Jan 2023 13:20:49 +0100 Subject: [PATCH] feat(combat): add possibility to cancel operation to quit the combat state early --- .../CombatManager.cs | 17 ++++++++++++++++- .../Operations/CompoundOperation.cs | 12 ++++++++++++ .../Operations/ICombatOperation.cs | 10 ++++++++++ .../Operations/UseItemOperation.cs | 12 +++++++++++- .../Operations/UseSkillOperation.cs | 9 +++++++++ .../Operations/WalkInRangeOperation.cs | 10 ++++++++++ .../Operations/WalkOperation.cs | 10 ++++++++++ 7 files changed, 78 insertions(+), 2 deletions(-) diff --git a/Extensions/NosSmooth.Extensions.Combat/CombatManager.cs b/Extensions/NosSmooth.Extensions.Combat/CombatManager.cs index 7becb911a0b0a85f49c3b0de94b28512dce4941f..46be264130d5238210d184bee44d4b861290065f 100644 --- a/Extensions/NosSmooth.Extensions.Combat/CombatManager.cs +++ b/Extensions/NosSmooth.Extensions.Combat/CombatManager.cs @@ -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) { diff --git a/Extensions/NosSmooth.Extensions.Combat/Operations/CompoundOperation.cs b/Extensions/NosSmooth.Extensions.Combat/Operations/CompoundOperation.cs index af73761687dddcd583ad07f12a513d07c5123d7b..687c4ad1ed6dc091af37481bf182a91609bc6b27 100644 --- a/Extensions/NosSmooth.Extensions.Combat/Operations/CompoundOperation.cs +++ b/Extensions/NosSmooth.Extensions.Combat/Operations/CompoundOperation.cs @@ -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? _compoundOperation; @@ -53,6 +54,9 @@ public class CompoundOperation : ICombatOperation /// public OperationQueueType QueueType => _queueType; + /// + public bool MayBeCancelled => _currentOperation?.MayBeCancelled ?? true; + /// public Task BeginExecution(ICombatState combatState, CancellationToken ct = default) { @@ -110,10 +114,18 @@ public class CompoundOperation : ICombatOperation public Result CanBeUsed(ICombatState combatState) => _operations[0].CanBeUsed(combatState); + /// + public void Cancel() + { + _currentOperation?.Cancel(); + _ct?.Cancel(); + } + private async Task UseAsync(ICombatState combatState, CancellationToken ct) { foreach (var operation in _operations) { + _currentOperation = operation; CanBeUsedResponse canBeUsed = CanBeUsedResponse.MustWait; while (canBeUsed != CanBeUsedResponse.CanBeUsed) diff --git a/Extensions/NosSmooth.Extensions.Combat/Operations/ICombatOperation.cs b/Extensions/NosSmooth.Extensions.Combat/Operations/ICombatOperation.cs index b9aabf03be0e050e1fb0beff9e40c663ac006be8..b2754fa36fa216e6f64d97c0d7b2f47e48276381 100644 --- a/Extensions/NosSmooth.Extensions.Combat/Operations/ICombatOperation.cs +++ b/Extensions/NosSmooth.Extensions.Combat/Operations/ICombatOperation.cs @@ -25,6 +25,11 @@ public interface ICombatOperation : IDisposable /// public OperationQueueType QueueType { get; } + /// + /// Gets whether the operation may currently be cancelled (disposed). + /// + public bool MayBeCancelled { get; } + /// /// Begin the execution without waiting for the finished state. /// @@ -63,4 +68,9 @@ public interface ICombatOperation : IDisposable /// The combat state. /// Whether the operation can be used right away. public Result CanBeUsed(ICombatState combatState); + + /// + /// Cancel the current operation. + /// + public void Cancel(); } \ No newline at end of file diff --git a/Extensions/NosSmooth.Extensions.Combat/Operations/UseItemOperation.cs b/Extensions/NosSmooth.Extensions.Combat/Operations/UseItemOperation.cs index 449e4c2b919e14eb1c2e1aa17bb985731ebc3e84..ee6a9b2973216010bc94d199f0c5f896be3d9b3b 100644 --- a/Extensions/NosSmooth.Extensions.Combat/Operations/UseItemOperation.cs +++ b/Extensions/NosSmooth.Extensions.Combat/Operations/UseItemOperation.cs @@ -26,6 +26,9 @@ public record UseItemOperation(InventoryItem Item) : ICombatOperation /// public OperationQueueType QueueType => OperationQueueType.Item; + /// + public bool MayBeCancelled => true; + /// public Task BeginExecution(ICombatState combatState, CancellationToken ct = default) { @@ -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(); @@ -82,10 +85,17 @@ public record UseItemOperation(InventoryItem Item) : ICombatOperation public Result CanBeUsed(ICombatState combatState) => Result.FromSuccess(); + /// + public void Cancel() + { + _ct?.Cancel(); + } + /// public void Dispose() { _ct?.Cancel(); _useItemOperation?.Dispose(); + _ct?.Dispose(); } } \ No newline at end of file diff --git a/Extensions/NosSmooth.Extensions.Combat/Operations/UseSkillOperation.cs b/Extensions/NosSmooth.Extensions.Combat/Operations/UseSkillOperation.cs index 14734af539b0821d7a85c7f7ee35ab77f62266c4..ae0b6feb94e00c7c77850338a1343485693e3a9d 100644 --- a/Extensions/NosSmooth.Extensions.Combat/Operations/UseSkillOperation.cs +++ b/Extensions/NosSmooth.Extensions.Combat/Operations/UseSkillOperation.cs @@ -39,6 +39,9 @@ public record UseSkillOperation /// public OperationQueueType QueueType => OperationQueueType.TotalControl; + /// + public bool MayBeCancelled => false; + /// public async Task BeginExecution(ICombatState combatState, CancellationToken ct = default) { @@ -143,6 +146,12 @@ public record UseSkillOperation return Result.FromSuccess(); } + /// + public void Cancel() + { + throw new InvalidOperationException(); + } + private Result> ContractSkill(ISkillInfo info) { if (info.AttackType == AttackType.Dash) diff --git a/Extensions/NosSmooth.Extensions.Combat/Operations/WalkInRangeOperation.cs b/Extensions/NosSmooth.Extensions.Combat/Operations/WalkInRangeOperation.cs index 36b0b61d5df3ac5eb8b40896de5d1ad669378783..d21d8f98b8db8e9b9cd482dc5bd287dfc39e54e9 100644 --- a/Extensions/NosSmooth.Extensions.Combat/Operations/WalkInRangeOperation.cs +++ b/Extensions/NosSmooth.Extensions.Combat/Operations/WalkInRangeOperation.cs @@ -35,6 +35,9 @@ public record WalkInRangeOperation /// public OperationQueueType QueueType => OperationQueueType.TotalControl; + /// + public bool MayBeCancelled => true; + /// public Task BeginExecution(ICombatState combatState, CancellationToken ct = default) { @@ -105,6 +108,12 @@ public record WalkInRangeOperation return Result.FromSuccess(); } + /// + public void Cancel() + { + _ct?.Cancel(); + } + private async Task 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 diff --git a/Extensions/NosSmooth.Extensions.Combat/Operations/WalkOperation.cs b/Extensions/NosSmooth.Extensions.Combat/Operations/WalkOperation.cs index 0797e676d10ac8661e27f76171cbe15f095c07e3..149c31fa844132aa9719f3f4aada48fceec792b7 100644 --- a/Extensions/NosSmooth.Extensions.Combat/Operations/WalkOperation.cs +++ b/Extensions/NosSmooth.Extensions.Combat/Operations/WalkOperation.cs @@ -25,6 +25,9 @@ public record WalkOperation(WalkManager WalkManager, short X, short Y) : ICombat /// public OperationQueueType QueueType => OperationQueueType.TotalControl; + /// + public bool MayBeCancelled => true; + /// public Task BeginExecution(ICombatState combatState, CancellationToken ct = default) { @@ -95,6 +98,12 @@ public record WalkOperation(WalkManager WalkManager, short X, short Y) : ICombat return Result.FromSuccess(); } + /// + public void Cancel() + { + _ct?.Cancel(); + } + private Task 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