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

@ -73,6 +73,7 @@ public static class GameEventWiring
// the player's own PropertyBundle (EncumbranceVal etc.) into the player ClientObject.
Func<uint>? playerGuid = null,
Action<uint /*weenieError*/>? onUseDone = null,
Action<AppraiseInfoParser.Parsed>? onAppraisal = null,
ItemManaState? itemMana = null,
Action<GameEvents.CharacterConfirmationRequest>? onConfirmationRequest = null,
Action<GameEvents.CharacterConfirmationDone>? onConfirmationDone = null,
@ -432,10 +433,18 @@ public static class GameEventWiring
registrar.Register(GameEventType.IdentifyObjectResponse, e =>
{
var p = AppraiseInfoParser.TryParse(e.Payload.Span);
if (p is null || !p.Value.Success) return;
if (p is null) return;
// Merge parsed properties into the item if we know about it.
if (items.Get(p.Value.Guid) is not null)
if (p.Value.Success && items.Get(p.Value.Guid) is not null)
items.UpdateProperties(p.Value.Guid, p.Value.Properties);
if (p.Value.CreatureProfile is { HealthMax: > 0u } creature)
combat.OnUpdateHealth(
p.Value.Guid,
Math.Clamp(
(float)creature.Health / creature.HealthMax,
0f,
1f));
onAppraisal?.Invoke(p.Value);
// Spellbook from appraise: for caster items / scrolls this is
// the cast-on-use list. The local player's full learned
// spellbook arrives via PlayerDescription (0x0013), which uses