feat(items): port retail external-container looting
Add the ClientUISystem ground-object lifecycle, authoritative root and nested ViewContents projections, replacement and close semantics, and the DAT-authored gmExternalContainerUI strip for chests and corpses. Route double-click loot and full or partial drag transfers through the shared retail item policy without optimistic external ownership. Remove the incorrect NoLongerViewingContents behavior from owned side packs and retire AP-106/#196. Release build succeeds and all 5,875 tests pass with five intentional skips. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
48a118db91
commit
20ce67b625
27 changed files with 1750 additions and 45 deletions
|
|
@ -0,0 +1,51 @@
|
|||
using AcDream.Core.Items;
|
||||
|
||||
namespace AcDream.App.World;
|
||||
|
||||
/// <summary>
|
||||
/// Owns the network side effect of replacing retail's current ground object.
|
||||
/// Presentation controllers observe the same state but do not own session
|
||||
/// lifecycle messages.
|
||||
/// </summary>
|
||||
public sealed class ExternalContainerLifecycleController : IDisposable
|
||||
{
|
||||
private readonly ExternalContainerState _state;
|
||||
private readonly ClientObjectTable _objects;
|
||||
private readonly Action<uint> _sendNoLongerViewingContents;
|
||||
private bool _disposed;
|
||||
|
||||
public ExternalContainerLifecycleController(
|
||||
ExternalContainerState state,
|
||||
ClientObjectTable objects,
|
||||
Action<uint> sendNoLongerViewingContents)
|
||||
{
|
||||
_state = state ?? throw new ArgumentNullException(nameof(state));
|
||||
_objects = objects ?? throw new ArgumentNullException(nameof(objects));
|
||||
_sendNoLongerViewingContents = sendNoLongerViewingContents
|
||||
?? throw new ArgumentNullException(nameof(sendNoLongerViewingContents));
|
||||
_state.Changed += OnChanged;
|
||||
}
|
||||
|
||||
private void OnChanged(ExternalContainerTransition transition)
|
||||
{
|
||||
// ClientUISystem::SetGroundObject @ 0x00564510 always queues the old
|
||||
// root's contents tree for destruction when the ground object changes.
|
||||
if (transition.PreviousContainerId != 0u)
|
||||
_objects.StopViewingContentsTree(transition.PreviousContainerId);
|
||||
|
||||
// Only replacement emits Event_NoLongerViewingContents. The authored
|
||||
// close/range path instead sends Use(root) and waits for event 0x0052.
|
||||
if (transition.Kind == ExternalContainerTransitionKind.Replaced
|
||||
&& transition.PreviousContainerId != 0u)
|
||||
{
|
||||
_sendNoLongerViewingContents(transition.PreviousContainerId);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
_state.Changed -= OnChanged;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue