Bind queued actions and pending inventory requests to exact live incarnations, separate optimistic placement from authoritative responses, and serialize retail-style inventory ownership across UI surfaces. Co-authored-by: OpenAI Codex <codex@openai.com>
68 lines
2.7 KiB
C#
68 lines
2.7 KiB
C#
using AcDream.App.Input;
|
|
using AcDream.Core.Physics;
|
|
using AcDream.Core.Physics.Motion;
|
|
|
|
namespace AcDream.App.Interaction;
|
|
|
|
internal interface IPlayerInteractionMovementSink
|
|
{
|
|
/// <summary>
|
|
/// Cancels any preceding MoveTo, invokes <paramref name="armAfterCancel"/>,
|
|
/// then installs the new movement. The callback boundary lets the intent
|
|
/// owner arm completion state before an already-facing TurnTo can finish
|
|
/// synchronously, without letting the preceding cancellation clear it.
|
|
/// </summary>
|
|
bool BeginApproach(InteractionApproach approach, Action? armAfterCancel = null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Installs retail's client-side TurnToObject/MoveToObject prediction through
|
|
/// the same MovementManager used by authoritative movement packets.
|
|
/// </summary>
|
|
internal sealed class PlayerInteractionMovementSink(
|
|
Func<PlayerMovementController?> player)
|
|
: IPlayerInteractionMovementSink
|
|
{
|
|
private readonly Func<PlayerMovementController?> _player = player
|
|
?? throw new ArgumentNullException(nameof(player));
|
|
|
|
public bool BeginApproach(InteractionApproach approach, Action? armAfterCancel = null)
|
|
{
|
|
PlayerMovementController? controller = _player();
|
|
if (controller?.MoveTo is null)
|
|
return false;
|
|
|
|
var parameters = new MovementParameters
|
|
{
|
|
DistanceToObject = approach.UseRadius,
|
|
CanCharge = approach.CanCharge,
|
|
};
|
|
var movement = new MovementStruct
|
|
{
|
|
ObjectId = approach.Target.ServerGuid,
|
|
TopLevelId = approach.Target.ServerGuid,
|
|
Pos = new Position(
|
|
approach.Player.CellId,
|
|
approach.Target.Entity.Position,
|
|
System.Numerics.Quaternion.Identity),
|
|
Params = parameters,
|
|
Type = approach.IsCloseRange
|
|
? MovementType.TurnToObject
|
|
: MovementType.MoveToObject,
|
|
Radius = approach.TargetRadius,
|
|
Height = approach.TargetHeight,
|
|
};
|
|
|
|
// PerformMovement cancels at its head. Do it explicitly before the
|
|
// intent is armed so cancellation of the preceding move cannot clear
|
|
// the new request; the internal second call is then a retail no-op.
|
|
controller.Movement.CancelMoveTo(WeenieError.ActionCancelled);
|
|
armAfterCancel?.Invoke();
|
|
|
|
// P1's wire MoveToObject store marks the command non-autonomous.
|
|
// The speculative local install must do the same or the next raw-input
|
|
// pump overwrites it before ACE's authoritative movement arrives.
|
|
controller.SetLastMoveWasAutonomous(false);
|
|
return controller.Movement.PerformMovement(movement) == WeenieError.None;
|
|
}
|
|
}
|