feat(ui): port retail appraisal panel

Preserve retail's one-pending-appraisal busy lifetime, parse the complete gated response, and mount the authored examination layout in the shared main-panel host. Keep known 3D preview and inscription-write gaps explicit in AP-110.
This commit is contained in:
Erik 2026-07-23 11:34:08 +02:00
parent 6b1ae4fb76
commit 643cdfe66e
24 changed files with 17132 additions and 40 deletions

View file

@ -234,6 +234,7 @@ public sealed class RetailUiRuntime : IDisposable
MountToolbar();
MountCombat();
MountSpellbook();
MountAppraisal();
MountEffects();
MountIndicatorDetailPanels();
MountIndicators();
@ -293,6 +294,7 @@ public sealed class RetailUiRuntime : IDisposable
public CombatUiController? CombatUiController { get; private set; }
public SpellcastingUiController? SpellcastingUiController { get; private set; }
public SpellbookWindowController? SpellbookWindowController { get; private set; }
public AppraisalUiController? AppraisalController { get; private set; }
public EffectsUiController? PositiveEffectsController { get; private set; }
public EffectsUiController? NegativeEffectsController { get; private set; }
public LinkStatusUiController? LinkStatusUiController { get; private set; }
@ -340,6 +342,7 @@ public sealed class RetailUiRuntime : IDisposable
FpsController?.Tick();
_vividTargetIndicator?.Tick();
SpellbookWindowController?.Tick();
AppraisalController?.Tick(deltaSeconds);
SpellcastingUiController?.Tick();
PositiveEffectsController?.Tick();
NegativeEffectsController?.Tick();
@ -391,6 +394,11 @@ public sealed class RetailUiRuntime : IDisposable
public bool HandleConfirmationDone(GameEvents.CharacterConfirmationDone done)
=> _gameplayConfirmationController?.HandleDone(done) == true;
public bool HandleAppraisal(AppraiseInfoParser.Parsed appraisal)
=> AppraisalController is { } controller
? controller.Apply(appraisal)
: ItemInteraction.AcceptAppraisalResponse(appraisal.Guid).Accepted;
public uint ShowConfirmation(string message, Action<bool> completed)
{
ArgumentNullException.ThrowIfNull(message);
@ -419,6 +427,13 @@ public sealed class RetailUiRuntime : IDisposable
}
}
public void ResetSessionTransientUi()
{
ResetSessionDialogs();
AppraisalController?.ResetSession();
_panelUi.SetPanelVisibility(RetailPanelCatalog.Examination, visible: false);
}
public void UpdateCursor(IEnumerable<IMouse> mice)
{
CursorFeedback feedback = _bindings.Cursor.Feedback.Update(Host.Root);
@ -938,6 +953,67 @@ public sealed class RetailUiRuntime : IDisposable
Console.WriteLine("[M3] retail spellbook/component book from LayoutDesc 0x21000034.");
}
private void MountAppraisal()
{
ImportedLayout? layout = Import(
AppraisalUiController.LayoutId,
AppraisalUiController.RootId);
if (layout is null)
{
Console.WriteLine(
"[M4] examination: LayoutDesc 0x2100006B root 0x100005F2 not found.");
return;
}
AppraisalUiController? controller = AppraisalUiController.Bind(
layout,
_bindings.Inventory.Objects,
_bindings.Inventory.ItemInteraction,
_bindings.Combat.State,
_bindings.Magic.Spellbook,
show: () => _panelUi.SetPanelVisibility(
RetailPanelCatalog.Examination,
visible: true),
close: () => CloseWindow(WindowNames.Examination));
if (controller is null)
{
Console.WriteLine(
"[M4] examination: required authored controls are missing.");
return;
}
AppraisalController = controller;
UiElement root = layout.Root;
RetailWindowHandle handle = RetailWindowFrame.Mount(
Host.Root,
root,
_bindings.Assets.ResolveSprite,
new RetailWindowFrame.Options
{
WindowName = WindowNames.Examination,
Chrome = RetailWindowChrome.Imported,
Left = root.Left,
Top = root.Top,
ContentWidth = root.Width,
ContentHeight = root.Height,
Visible = false,
ResizeX = true,
ResizeY = true,
MinWidth = 310f,
MinHeight = 400f,
ConstrainDragToParent = true,
ConstrainResizeToParent = true,
ContentClickThrough = false,
Controller = controller,
});
_panelUi.RegisterMainPanel(
RetailPanelCatalog.Examination,
WindowNames.Examination,
handle);
Console.WriteLine(
"[M4] retail examination window from LayoutDesc 0x2100006B.");
}
private void MountEffects()
{
MountEffectsInstance(positive: true);