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