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
|
|
@ -22,6 +22,7 @@ internal sealed class SelectionInteractionController
|
|||
private readonly ISelectionInteractionTransport _transport;
|
||||
private readonly IPlayerInteractionMovementSink _movement;
|
||||
private readonly OutboundInteractionQueue _outbound = new();
|
||||
private readonly PlayerApproachCompletionState _approachCompletions;
|
||||
private readonly Action<string>? _toast;
|
||||
private PendingPostArrivalAction? _pendingPostArrival;
|
||||
|
||||
|
|
@ -36,7 +37,8 @@ internal sealed class SelectionInteractionController
|
|||
bool IsPickup,
|
||||
uint DestinationContainerId,
|
||||
int Placement,
|
||||
ulong PendingPlacementToken);
|
||||
ulong PendingPlacementToken,
|
||||
PlayerApproachToken ApproachToken);
|
||||
|
||||
public SelectionInteractionController(
|
||||
SelectionState selection,
|
||||
|
|
@ -44,7 +46,8 @@ internal sealed class SelectionInteractionController
|
|||
ItemInteractionController items,
|
||||
ISelectionInteractionTransport transport,
|
||||
IPlayerInteractionMovementSink movement,
|
||||
Action<string>? toast = null)
|
||||
Action<string>? toast = null,
|
||||
PlayerApproachCompletionState? approachCompletions = null)
|
||||
{
|
||||
_selection = selection ?? throw new ArgumentNullException(nameof(selection));
|
||||
_query = query ?? throw new ArgumentNullException(nameof(query));
|
||||
|
|
@ -52,6 +55,8 @@ internal sealed class SelectionInteractionController
|
|||
_transport = transport ?? throw new ArgumentNullException(nameof(transport));
|
||||
_movement = movement ?? throw new ArgumentNullException(nameof(movement));
|
||||
_toast = toast;
|
||||
_approachCompletions = approachCompletions
|
||||
?? new PlayerApproachCompletionState();
|
||||
}
|
||||
|
||||
public bool HandleInputAction(InputAction action)
|
||||
|
|
@ -213,11 +218,12 @@ internal sealed class SelectionInteractionController
|
|||
IsPickup: false,
|
||||
DestinationContainerId: 0u,
|
||||
Placement: 0,
|
||||
PendingPlacementToken: 0u);
|
||||
PendingPlacementToken: 0u,
|
||||
ApproachToken: default);
|
||||
bool started = _movement.BeginApproach(
|
||||
approach,
|
||||
() => _pendingPostArrival = pending);
|
||||
if (!started && _pendingPostArrival == pending)
|
||||
token => _pendingPostArrival = pending with { ApproachToken = token });
|
||||
if (!started && _pendingPostArrival?.ServerGuid == pending.ServerGuid)
|
||||
CancelPendingApproach();
|
||||
return;
|
||||
}
|
||||
|
|
@ -265,11 +271,12 @@ internal sealed class SelectionInteractionController
|
|||
IsPickup: true,
|
||||
destinationContainerId,
|
||||
placement,
|
||||
pendingPlacementToken);
|
||||
pendingPlacementToken,
|
||||
ApproachToken: default);
|
||||
bool started = _movement.BeginApproach(
|
||||
approach,
|
||||
() => _pendingPostArrival = pending);
|
||||
if (!started && _pendingPostArrival == pending)
|
||||
token => _pendingPostArrival = pending with { ApproachToken = token });
|
||||
if (!started && _pendingPostArrival?.ServerGuid == pending.ServerGuid)
|
||||
CancelPendingApproach();
|
||||
else if (!started)
|
||||
CancelPickupPresentation(itemGuid, pendingPlacementToken);
|
||||
|
|
@ -307,6 +314,9 @@ internal sealed class SelectionInteractionController
|
|||
|
||||
/// <summary>Fires only after natural MoveToComplete(None), never cancellation.</summary>
|
||||
public void OnNaturalMoveToComplete()
|
||||
=> HandleNaturalMoveToComplete();
|
||||
|
||||
private void HandleNaturalMoveToComplete()
|
||||
{
|
||||
if (_pendingPostArrival is not { } pending)
|
||||
return;
|
||||
|
|
@ -351,7 +361,19 @@ internal sealed class SelectionInteractionController
|
|||
}
|
||||
}
|
||||
|
||||
public void DrainOutbound() => _outbound.Drain();
|
||||
public void DrainOutbound()
|
||||
{
|
||||
while (_approachCompletions.TryTake(out PlayerApproachCompletion completion))
|
||||
{
|
||||
if (_pendingPostArrival?.ApproachToken != completion.Token)
|
||||
continue;
|
||||
if (completion.IsNatural)
|
||||
HandleNaturalMoveToComplete();
|
||||
else
|
||||
CancelPendingApproach();
|
||||
}
|
||||
_outbound.Drain();
|
||||
}
|
||||
|
||||
public void OnMoveToCancelled(WeenieError _) => CancelPendingApproach();
|
||||
|
||||
|
|
@ -395,6 +417,8 @@ internal sealed class SelectionInteractionController
|
|||
catch (Exception error) { failures.Add(error); }
|
||||
try { _outbound.Clear(); }
|
||||
catch (Exception error) { failures.Add(error); }
|
||||
try { _approachCompletions.Clear(); }
|
||||
catch (Exception error) { failures.Add(error); }
|
||||
_pendingPostArrival = null;
|
||||
|
||||
if (failures.Count != 0)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue