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
98
src/AcDream.Core/Items/ExternalContainerState.cs
Normal file
98
src/AcDream.Core/Items/ExternalContainerState.cs
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
using System;
|
||||
|
||||
namespace AcDream.Core.Items;
|
||||
|
||||
public enum ExternalContainerTransitionKind
|
||||
{
|
||||
Opened,
|
||||
Replaced,
|
||||
Closed,
|
||||
Reset,
|
||||
}
|
||||
|
||||
public readonly record struct ExternalContainerTransition(
|
||||
ExternalContainerTransitionKind Kind,
|
||||
uint PreviousContainerId,
|
||||
uint ContainerId);
|
||||
|
||||
/// <summary>
|
||||
/// Owns retail <c>ClientUISystem::groundObject</c> and the outstanding external
|
||||
/// container request. ACE sends ViewContents for the requested root and then for
|
||||
/// nested containers; only the expected root may become the visible ground object.
|
||||
/// Retail: <c>ClientUISystem::OnViewContents @ 0x00565870</c> and
|
||||
/// <c>SetGroundObject @ 0x00564510</c>.
|
||||
/// </summary>
|
||||
public sealed class ExternalContainerState
|
||||
{
|
||||
public uint RequestedContainerId { get; private set; }
|
||||
public uint CurrentContainerId { get; private set; }
|
||||
|
||||
public event Action<ExternalContainerTransition>? Changed;
|
||||
|
||||
public bool RequestOpen(uint containerId)
|
||||
{
|
||||
if (containerId == 0u || RequestedContainerId == containerId)
|
||||
return false;
|
||||
RequestedContainerId = containerId;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ApplyViewContents(uint containerId)
|
||||
{
|
||||
if (containerId == 0u || containerId != RequestedContainerId)
|
||||
return false;
|
||||
|
||||
uint previous = CurrentContainerId;
|
||||
CurrentContainerId = containerId;
|
||||
RequestedContainerId = containerId;
|
||||
if (previous == containerId)
|
||||
return false;
|
||||
|
||||
Changed?.Invoke(new ExternalContainerTransition(
|
||||
previous == 0u
|
||||
? ExternalContainerTransitionKind.Opened
|
||||
: ExternalContainerTransitionKind.Replaced,
|
||||
previous,
|
||||
containerId));
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ApplyClose(uint containerId)
|
||||
{
|
||||
if (containerId == 0u || containerId != CurrentContainerId)
|
||||
return false;
|
||||
|
||||
uint previous = CurrentContainerId;
|
||||
CurrentContainerId = 0u;
|
||||
RequestedContainerId = 0u;
|
||||
Changed?.Invoke(new ExternalContainerTransition(
|
||||
ExternalContainerTransitionKind.Closed,
|
||||
previous,
|
||||
0u));
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ApplyUseDone(uint weenieError)
|
||||
{
|
||||
if (weenieError == 0u || RequestedContainerId == CurrentContainerId)
|
||||
return false;
|
||||
RequestedContainerId = CurrentContainerId;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Reset()
|
||||
{
|
||||
uint previous = CurrentContainerId;
|
||||
bool changed = previous != 0u || RequestedContainerId != 0u;
|
||||
CurrentContainerId = 0u;
|
||||
RequestedContainerId = 0u;
|
||||
if (changed)
|
||||
{
|
||||
Changed?.Invoke(new ExternalContainerTransition(
|
||||
ExternalContainerTransitionKind.Reset,
|
||||
previous,
|
||||
0u));
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue