fix(items): finish corpse pickup and combat switching
Port retail's first-slot ShowPendingInPlayer path for double-click loot and carry the current owned-container destination through deferred pickup. Retire the previous ground-container view as soon as a replacement is requested so its range close cannot cancel ACE's active MoveTo chain. Preserve active-combat weapon intent across ACE's authoritative wand-to-missile stance tail while leaving peace-mode switches unchanged. Release build succeeds and all 5,885 tests pass with five intentional skips. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
d51a0fc825
commit
0392c6d721
17 changed files with 631 additions and 81 deletions
|
|
@ -1136,7 +1136,13 @@ public sealed class GameWindow : IDisposable
|
|||
// rotating to face the target). Only set for close-range Use/PickUp;
|
||||
// far-range sends fire the wire packet immediately at SendUse/SendPickUp
|
||||
// time. Cleared before the deferred send fires — single-fire, no retry.
|
||||
private (uint Guid, bool IsPickup)? _pendingPostArrivalAction;
|
||||
private PendingPostArrivalAction? _pendingPostArrivalAction;
|
||||
|
||||
private readonly record struct PendingPostArrivalAction(
|
||||
uint Guid,
|
||||
bool IsPickup,
|
||||
uint DestinationContainerId = 0u,
|
||||
int Placement = 0);
|
||||
|
||||
// R5-V2: the local player's CPhysicsObj stand-in — owns the player's
|
||||
// TargetManager voyeur system, registered in _physicsHosts so remote
|
||||
|
|
@ -2127,7 +2133,13 @@ public sealed class GameWindow : IDisposable
|
|||
_itemInteractionController = new AcDream.App.UI.ItemInteractionController(
|
||||
Objects,
|
||||
playerGuid: () => _playerServerGuid,
|
||||
sendUse: g => _liveSession?.SendUse(g),
|
||||
// ItemHolder::UseObject is the common policy owner, but world
|
||||
// activation still has to pass through the application-level
|
||||
// use adapter. It owns speculative turn/move, the close-range
|
||||
// deferred send, and ACE's far-range MoveTo callback. Calling
|
||||
// WorldSession directly here made keyboard R approach a corpse
|
||||
// without completing the same open transaction as double-click.
|
||||
sendUse: SendUse,
|
||||
sendExamine: g => _liveSession?.SendAppraise(g),
|
||||
sendUseWithTarget: (source, target) => _liveSession?.SendUseWithTarget(source, target),
|
||||
sendWield: (item, mask) => _liveSession?.SendGetAndWieldItem(item, mask),
|
||||
|
|
@ -2144,8 +2156,19 @@ public sealed class GameWindow : IDisposable
|
|||
playerOnGround: GetDebugPlayerOnGround,
|
||||
inNonCombatMode: () => Combat.CurrentMode
|
||||
== AcDream.Core.Combat.CombatMode.NonCombat,
|
||||
combatState: Combat,
|
||||
sendChangeCombatMode: mode =>
|
||||
_liveSession?.SendChangeCombatMode(mode),
|
||||
isComponentPack: magicCatalog.IsComponentPack,
|
||||
placeInBackpack: SendPickUp,
|
||||
backpackContainerId: () =>
|
||||
_retailUiRuntime?.InventoryPanelController?.CurrentOpenContainerId
|
||||
?? _playerServerGuid,
|
||||
// Retail ItemHolder::DetermineUseResult treats children of the
|
||||
// current ground object as loot. Keep this late-bound because
|
||||
// ViewContents can replace/close the external container while
|
||||
// the retained controller remains alive.
|
||||
groundObjectId: () => _externalContainers.CurrentContainerId,
|
||||
sendSplitToWorld: (item, amount) =>
|
||||
_liveSession?.SendStackableSplitTo3D(item, amount),
|
||||
selectedObjectId: () => _selection.SelectedObjectId ?? 0u,
|
||||
|
|
@ -13258,9 +13281,6 @@ public sealed class GameWindow : IDisposable
|
|||
return;
|
||||
}
|
||||
|
||||
if (IsExternalContainerTarget(guid))
|
||||
_externalContainers.RequestOpen(guid);
|
||||
|
||||
// B.6/R4-V5: install a speculative local TurnToObject/MoveToObject
|
||||
// through the player's MoveToManager so close-range Use rotates the
|
||||
// body to face before the action fires. For FAR targets, ACE's
|
||||
|
|
@ -13278,7 +13298,7 @@ public sealed class GameWindow : IDisposable
|
|||
{
|
||||
// Defer the wire packet — OnAutoWalkArrivedSendDeferredAction
|
||||
// will fire it after rotation completes.
|
||||
_pendingPostArrivalAction = (guid, false);
|
||||
_pendingPostArrivalAction = new PendingPostArrivalAction(guid, IsPickup: false);
|
||||
if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeAutoWalkEnabled)
|
||||
Console.WriteLine($"[B.4b] use deferred (close-range, turn-first) guid=0x{guid:X8}");
|
||||
return;
|
||||
|
|
@ -13318,6 +13338,9 @@ public sealed class GameWindow : IDisposable
|
|||
}
|
||||
|
||||
private void SendPickUp(uint itemGuid)
|
||||
=> SendPickUp(itemGuid, _playerServerGuid, placement: 0);
|
||||
|
||||
private void SendPickUp(uint itemGuid, uint destinationContainerId, int placement)
|
||||
{
|
||||
if (_liveSession is null
|
||||
|| _liveSession.CurrentState != AcDream.Core.Net.WorldSession.State.InWorld)
|
||||
|
|
@ -13362,7 +13385,11 @@ public sealed class GameWindow : IDisposable
|
|||
|
||||
if (closeRange)
|
||||
{
|
||||
_pendingPostArrivalAction = (itemGuid, true);
|
||||
_pendingPostArrivalAction = new PendingPostArrivalAction(
|
||||
itemGuid,
|
||||
IsPickup: true,
|
||||
destinationContainerId,
|
||||
placement);
|
||||
if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeAutoWalkEnabled)
|
||||
Console.WriteLine($"[B.5] pickup deferred (close-range, turn-first) item=0x{itemGuid:X8}");
|
||||
return;
|
||||
|
|
@ -13375,9 +13402,9 @@ public sealed class GameWindow : IDisposable
|
|||
// ACE's chain alive.
|
||||
var seq = _liveSession.NextGameActionSequence();
|
||||
var body = AcDream.Core.Net.Messages.InteractRequests.BuildPickUp(
|
||||
seq, itemGuid, _playerServerGuid, placement: 0);
|
||||
seq, itemGuid, destinationContainerId, placement);
|
||||
_liveSession.SendGameAction(body);
|
||||
Console.WriteLine($"[B.5] pickup item=0x{itemGuid:X8} container=0x{_playerServerGuid:X8} seq={seq}");
|
||||
Console.WriteLine($"[B.5] pickup item=0x{itemGuid:X8} container=0x{destinationContainerId:X8} placement={placement} seq={seq}");
|
||||
if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeAutoWalkEnabled)
|
||||
{
|
||||
string label = DescribeLiveEntity(itemGuid);
|
||||
|
|
@ -13398,7 +13425,7 @@ public sealed class GameWindow : IDisposable
|
|||
/// </summary>
|
||||
private void OnAutoWalkArrivedSendDeferredAction()
|
||||
{
|
||||
if (_pendingPostArrivalAction is not (uint guid, bool isPickup) pending)
|
||||
if (_pendingPostArrivalAction is not { } pending)
|
||||
return;
|
||||
_pendingPostArrivalAction = null;
|
||||
|
||||
|
|
@ -13407,18 +13434,21 @@ public sealed class GameWindow : IDisposable
|
|||
return;
|
||||
|
||||
var seq = _liveSession.NextGameActionSequence();
|
||||
if (isPickup)
|
||||
if (pending.IsPickup)
|
||||
{
|
||||
var body = AcDream.Core.Net.Messages.InteractRequests.BuildPickUp(
|
||||
seq, guid, _playerServerGuid, placement: 0);
|
||||
seq,
|
||||
pending.Guid,
|
||||
pending.DestinationContainerId,
|
||||
pending.Placement);
|
||||
_liveSession.SendGameAction(body);
|
||||
Console.WriteLine($"[B.5] pickup-deferred item=0x{guid:X8} container=0x{_playerServerGuid:X8} seq={seq}");
|
||||
Console.WriteLine($"[B.5] pickup-deferred item=0x{pending.Guid:X8} container=0x{pending.DestinationContainerId:X8} placement={pending.Placement} seq={seq}");
|
||||
}
|
||||
else
|
||||
{
|
||||
var body = AcDream.Core.Net.Messages.InteractRequests.BuildUse(seq, guid);
|
||||
var body = AcDream.Core.Net.Messages.InteractRequests.BuildUse(seq, pending.Guid);
|
||||
_liveSession.SendGameAction(body);
|
||||
Console.WriteLine($"[B.4b] use-deferred guid=0x{guid:X8} seq={seq}");
|
||||
Console.WriteLine($"[B.4b] use-deferred guid=0x{pending.Guid:X8} seq={seq}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -13497,20 +13527,6 @@ public sealed class GameWindow : IDisposable
|
|||
return dx * dx + dy * dy <= useRadius * useRadius;
|
||||
}
|
||||
|
||||
private bool IsExternalContainerTarget(uint guid)
|
||||
{
|
||||
if (Objects.Get(guid) is { } item
|
||||
&& (item.ContainerTypeHint != 0u
|
||||
|| item.Type.HasFlag(AcDream.Core.Items.ItemType.Container)
|
||||
|| item.ItemsCapacity > 0))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return LastSpawns.TryGetValue(guid, out var spawn)
|
||||
&& (spawn.ObjectDescriptionFlags.GetValueOrDefault() & 0x2000u) != 0u;
|
||||
}
|
||||
|
||||
private void InstallSpeculativeTurnToTarget(uint targetGuid)
|
||||
{
|
||||
if (_playerController is not { } pc || pc.MoveTo is null) return;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue