fix(items): preserve wand stance and corpse use order
Keep active-combat AutoWield intent through ACE's pre-wield transition and queued trailing peace notice, while allowing explicit combat input to supersede it. Queue one-shot item interactions until the frame's movement edge has been serialized, so a movement release cannot cancel ACE's server-side corpse approach callback. Release build succeeds and all 5,890 runnable tests pass with five intentional skips. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
0392c6d721
commit
e6dd8bf6fa
10 changed files with 303 additions and 22 deletions
|
|
@ -69,6 +69,7 @@ internal sealed class AutoWieldController : IDisposable
|
|||
|
||||
private PendingSwitch? _pendingSwitch;
|
||||
private PendingCombatSettlement? _pendingCombatSettlement;
|
||||
private bool _combatTransitionObservedDuringSwitch;
|
||||
private bool _disposed;
|
||||
|
||||
public bool IsBusy => _pendingSwitch is not null;
|
||||
|
|
@ -109,6 +110,7 @@ internal sealed class AutoWieldController : IDisposable
|
|||
public bool TryWield(ClientObject item)
|
||||
{
|
||||
_pendingCombatSettlement = null;
|
||||
_combatTransitionObservedDuringSwitch = false;
|
||||
return TryWield(item, EquipMask.None, combatModeAfterWield: null);
|
||||
}
|
||||
|
||||
|
|
@ -121,6 +123,7 @@ internal sealed class AutoWieldController : IDisposable
|
|||
public bool TryWield(ClientObject item, EquipMask requestedMask)
|
||||
{
|
||||
_pendingCombatSettlement = null;
|
||||
_combatTransitionObservedDuringSwitch = false;
|
||||
return TryWield(item, requestedMask, combatModeAfterWield: null);
|
||||
}
|
||||
|
||||
|
|
@ -309,18 +312,32 @@ internal sealed class AutoWieldController : IDisposable
|
|||
_pendingCombatSettlement = new(
|
||||
itemId,
|
||||
readyMode,
|
||||
CombatSettlementPhase.AwaitingPostWieldMode);
|
||||
CombatSettlementPhase.AwaitingPostWieldMode,
|
||||
_combatTransitionObservedDuringSwitch);
|
||||
}
|
||||
_combatTransitionObservedDuringSwitch = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCombatModeChanged(CombatMode mode)
|
||||
{
|
||||
if (_pendingSwitch is { CombatModeAfterWield: { } switchReadyMode }
|
||||
&& mode != switchReadyMode)
|
||||
{
|
||||
// ACE's blocker/dequip callbacks can begin their old-stance ->
|
||||
// Peace shuffle before WieldObject for the replacement arrives.
|
||||
// Remembering that authoritative transition lets the settlement
|
||||
// owner distinguish a later ready-mode notice followed by ACE's
|
||||
// queued trailing Peace from a server that settled directly.
|
||||
_combatTransitionObservedDuringSwitch = true;
|
||||
}
|
||||
|
||||
if (_pendingCombatSettlement is not { } settlement)
|
||||
return;
|
||||
if (mode == settlement.ReadyMode)
|
||||
{
|
||||
if (settlement.Phase == CombatSettlementPhase.SawTransitionalPeace)
|
||||
if (settlement.Phase == CombatSettlementPhase.SawTransitionalPeace
|
||||
|| settlement.ExpectTrailingPeace)
|
||||
{
|
||||
// ACE selected the correct weapon stance, but its queued
|
||||
// dequip callback still has one trailing Peace notice.
|
||||
|
|
@ -365,7 +382,10 @@ internal sealed class AutoWieldController : IDisposable
|
|||
if (_pendingSwitch is { } pending
|
||||
&& (failure.ItemId == pending.BlockingItemId
|
||||
|| failure.ItemId == pending.RequestedItemId))
|
||||
{
|
||||
_pendingSwitch = null;
|
||||
_combatTransitionObservedDuringSwitch = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnObjectRemoved(ClientObject item)
|
||||
|
|
@ -373,7 +393,10 @@ internal sealed class AutoWieldController : IDisposable
|
|||
if (_pendingSwitch is { } pending
|
||||
&& (item.ObjectId == pending.BlockingItemId
|
||||
|| item.ObjectId == pending.RequestedItemId))
|
||||
{
|
||||
_pendingSwitch = null;
|
||||
_combatTransitionObservedDuringSwitch = false;
|
||||
}
|
||||
if (_pendingCombatSettlement is { } settlement
|
||||
&& item.ObjectId == settlement.ItemId)
|
||||
_pendingCombatSettlement = null;
|
||||
|
|
@ -383,6 +406,20 @@ internal sealed class AutoWieldController : IDisposable
|
|||
{
|
||||
_pendingSwitch = null;
|
||||
_pendingCombatSettlement = null;
|
||||
_combatTransitionObservedDuringSwitch = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A user combat-mode command supersedes any ready-mode intent retained by
|
||||
/// an in-flight AutoWield transaction. This prevents the ACE compatibility
|
||||
/// settlement from fighting a deliberate request for Peace.
|
||||
/// </summary>
|
||||
public void NotifyExplicitCombatModeRequest()
|
||||
{
|
||||
_pendingCombatSettlement = null;
|
||||
_combatTransitionObservedDuringSwitch = false;
|
||||
if (_pendingSwitch is { } pending)
|
||||
_pendingSwitch = pending with { CombatModeAfterWield = null };
|
||||
}
|
||||
|
||||
private EquipMask BestAvailableEquipMask(ClientObject item)
|
||||
|
|
@ -496,6 +533,7 @@ internal sealed class AutoWieldController : IDisposable
|
|||
_disposed = true;
|
||||
_pendingSwitch = null;
|
||||
_pendingCombatSettlement = null;
|
||||
_combatTransitionObservedDuringSwitch = false;
|
||||
_objects.ObjectMoved -= OnObjectMoved;
|
||||
_objects.ObjectRemoved -= OnObjectRemoved;
|
||||
_objects.MoveRequestFailed -= OnMoveRequestFailed;
|
||||
|
|
@ -514,7 +552,8 @@ internal sealed class AutoWieldController : IDisposable
|
|||
private readonly record struct PendingCombatSettlement(
|
||||
uint ItemId,
|
||||
CombatMode ReadyMode,
|
||||
CombatSettlementPhase Phase);
|
||||
CombatSettlementPhase Phase,
|
||||
bool ExpectTrailingPeace);
|
||||
|
||||
private enum CombatSettlementPhase
|
||||
{
|
||||
|
|
|
|||
|
|
@ -317,6 +317,10 @@ public sealed class ItemInteractionController : IDisposable
|
|||
return _autoWield.TryWield(item, targetMask);
|
||||
}
|
||||
|
||||
/// <summary>User combat-mode input supersedes AutoWield's retained mode.</summary>
|
||||
public void NotifyExplicitCombatModeRequest()
|
||||
=> _autoWield.NotifyExplicitCombatModeRequest();
|
||||
|
||||
public bool AcquireTarget(uint targetGuid)
|
||||
{
|
||||
if (!IsTargetModeActive || targetGuid == 0) return false;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue