refactor(runtime): extract local teleport and player mode
Move local teleport, player-mode, animation, shadow, and sealed-dungeon lifetimes out of GameWindow. Make player-mode entry transactional and isolate approach completions by controller lifetime and approach generation so stale callbacks cannot affect replacements. Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
parent
c557038353
commit
eeb0f6b45c
29 changed files with 3311 additions and 1073 deletions
|
|
@ -104,17 +104,20 @@ public sealed class SelectionInteractionControllerTests
|
|||
}
|
||||
}
|
||||
|
||||
private sealed class Movement : IPlayerInteractionMovementSink
|
||||
private sealed class Movement(IPlayerApproachTokenSource approachTokens)
|
||||
: IPlayerInteractionMovementSink
|
||||
{
|
||||
public List<InteractionApproach> Approaches { get; } = new();
|
||||
public bool Starts { get; set; } = true;
|
||||
public Action? AfterArm { get; set; }
|
||||
public bool BeginApproach(InteractionApproach approach, Action? armAfterCancel = null)
|
||||
public bool BeginApproach(
|
||||
InteractionApproach approach,
|
||||
Action<PlayerApproachToken>? armAfterCancel = null)
|
||||
{
|
||||
Approaches.Add(approach);
|
||||
if (!Starts)
|
||||
if (!Starts || !approachTokens.TryBeginApproach(out PlayerApproachToken token))
|
||||
return false;
|
||||
armAfterCancel?.Invoke();
|
||||
armAfterCancel?.Invoke(token);
|
||||
AfterArm?.Invoke();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -124,7 +127,9 @@ public sealed class SelectionInteractionControllerTests
|
|||
{
|
||||
public readonly Query Query = new();
|
||||
public readonly Transport Transport = new();
|
||||
public readonly Movement Movement = new();
|
||||
public readonly PlayerApproachCompletionState Completions = new();
|
||||
public readonly IPlayerApproachCompletionSink CompletionLifetime;
|
||||
public readonly Movement Movement;
|
||||
public readonly SelectionState Selection = new();
|
||||
public readonly ClientObjectTable Objects = new();
|
||||
public readonly List<string> Toasts = new();
|
||||
|
|
@ -136,6 +141,8 @@ public sealed class SelectionInteractionControllerTests
|
|||
|
||||
public Harness()
|
||||
{
|
||||
CompletionLifetime = Completions.BeginControllerLifetime();
|
||||
Movement = new Movement(Completions);
|
||||
SelectionInteractionController? controller = null;
|
||||
Objects.AddOrUpdate(new ClientObject
|
||||
{
|
||||
|
|
@ -168,7 +175,8 @@ public sealed class SelectionInteractionControllerTests
|
|||
Items,
|
||||
Transport,
|
||||
Movement,
|
||||
Toasts.Add);
|
||||
Toasts.Add,
|
||||
Completions);
|
||||
Items.PendingBackpackPlacementRequested += PendingPlacements.Add;
|
||||
Items.PendingBackpackPlacementCancelled += CancelledPlacements.Add;
|
||||
}
|
||||
|
|
@ -386,6 +394,47 @@ public sealed class SelectionInteractionControllerTests
|
|||
Assert.Empty(h.Transport.Uses);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public void DeferredCompletionFromPriorApproachCannotAffectReplacement(
|
||||
bool priorCompletedNaturally)
|
||||
{
|
||||
var h = new Harness();
|
||||
h.SetApproach(closeRange: true);
|
||||
h.Controller.SendUse(Target);
|
||||
if (priorCompletedNaturally)
|
||||
h.CompletionLifetime.PublishNaturalCompletion();
|
||||
else
|
||||
h.CompletionLifetime.PublishCancellation(WeenieError.ActionCancelled);
|
||||
|
||||
h.Controller.SendUse(Target);
|
||||
h.Controller.DrainOutbound();
|
||||
|
||||
Assert.Empty(h.Transport.Uses);
|
||||
|
||||
h.CompletionLifetime.PublishNaturalCompletion();
|
||||
h.Controller.DrainOutbound();
|
||||
|
||||
Assert.Equal(new[] { Target }, h.Transport.Uses);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RetiringControllerLifetimeInvalidatesQueuedArrival()
|
||||
{
|
||||
var h = new Harness();
|
||||
h.SetApproach(closeRange: true);
|
||||
h.Controller.SendUse(Target);
|
||||
h.CompletionLifetime.PublishNaturalCompletion();
|
||||
|
||||
h.Completions.RetireControllerLifetime(h.CompletionLifetime);
|
||||
h.Controller.DrainOutbound();
|
||||
|
||||
Assert.Empty(h.Transport.Uses);
|
||||
h.Controller.OnNaturalMoveToComplete();
|
||||
Assert.Empty(h.Transport.Uses);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FailedClosePickupStartWithdrawsPresentation()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue