feat(ui): port retail item appraisal reports

Follow ItemExamineUI's EoR dispatch and wording for item assessment instead of the generic projection. Resolve material and creature names through installed DAT maps, cover specialized item branches and item-XP curves, and narrow AP-110 to the remaining live/localized preview seams.
This commit is contained in:
Erik 2026-07-23 19:08:05 +02:00
parent 2c00d53db2
commit d78d09cfbc
12 changed files with 1258 additions and 193 deletions

View file

@ -63,6 +63,7 @@ public sealed class AppraisalUiController : IRetainedPanelController
private readonly CreatureAppraisalLayeredList? _creatureExtra;
private readonly CreatureAppraisalRowTemplateFactory? _creatureRowTemplates;
private readonly CreatureDisplayNameResolver _creatureNames;
private readonly RetailAppraisalNameResolver _itemNames;
private AppraisalView _activeView;
private uint _itemObjectId;
private uint _creatureObjectId;
@ -95,7 +96,8 @@ public sealed class AppraisalUiController : IRetainedPanelController
UiText title,
UiText itemText,
CreatureAppraisalRowTemplateFactory? creatureRowTemplates,
CreatureDisplayNameResolver? creatureNames)
CreatureDisplayNameResolver? creatureNames,
RetailAppraisalNameResolver? itemNames)
{
_layout = layout;
_objects = objects;
@ -117,6 +119,7 @@ public sealed class AppraisalUiController : IRetainedPanelController
_creatureNames = creatureNames
?? new CreatureDisplayNameResolver(
new Dictionary<uint, string>());
_itemNames = itemNames ?? RetailAppraisalNameResolver.Empty;
_inscriptionText = layout.FindElement(InscriptionTextId) as UiText;
_inscriptionField = layout.FindElement(InscriptionTextId) as UiField;
_signature = layout.FindElement(SignatureTextId) as UiText;
@ -200,7 +203,8 @@ public sealed class AppraisalUiController : IRetainedPanelController
Action show,
Action close,
CreatureAppraisalRowTemplateFactory? creatureRowTemplates = null,
CreatureDisplayNameResolver? creatureNames = null)
CreatureDisplayNameResolver? creatureNames = null,
RetailAppraisalNameResolver? itemNames = null)
{
ArgumentNullException.ThrowIfNull(layout);
ArgumentNullException.ThrowIfNull(objects);
@ -237,7 +241,8 @@ public sealed class AppraisalUiController : IRetainedPanelController
title,
itemText,
creatureRowTemplates,
creatureNames);
creatureNames,
itemNames);
}
/// <summary>
@ -343,7 +348,8 @@ public sealed class AppraisalUiController : IRetainedPanelController
_itemReport = ItemAppraisalTextFormatter.BuildReport(
obj,
appraisal,
ResolveSpell);
ResolveSpell,
_itemNames);
SetInscription(obj, appraisal);
if (newlySelected)
{

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,91 @@
using AcDream.Content;
using DatReaderWriter;
using DatReaderWriter.DBObjs;
using DatReaderWriter.Types;
namespace AcDream.App.UI.Layout;
/// <summary>
/// Resolves the authored enum display names consumed by retail's appraisal
/// helpers. Material names follow
/// <c>MaterialTypeEnumMapper::MaterialTypeToString @ 0x005CD500</c>;
/// creature and heritage names follow
/// <c>AppraisalSystem::InqCreatureDisplayName @ 0x005B59E0</c> and
/// <c>InqHeritageGroupDisplayName @ 0x005B4710</c>.
/// </summary>
public sealed class RetailAppraisalNameResolver
{
private const uint MaterialClientEnum = 0x10000001u;
private const uint MaterialSubEnum = 1u;
public static RetailAppraisalNameResolver Empty { get; } = new(
new Dictionary<uint, string>(),
new CreatureDisplayNameResolver(new Dictionary<uint, string>()));
private readonly IReadOnlyDictionary<uint, string> _materials;
private readonly CreatureDisplayNameResolver _creatures;
public RetailAppraisalNameResolver(
IReadOnlyDictionary<uint, string> materials,
CreatureDisplayNameResolver creatures)
{
_materials = materials
?? throw new ArgumentNullException(nameof(materials));
_creatures = creatures
?? throw new ArgumentNullException(nameof(creatures));
}
public static RetailAppraisalNameResolver Load(
IDatReaderWriter dats,
CreatureDisplayNameResolver creatures)
{
ArgumentNullException.ThrowIfNull(dats);
ArgumentNullException.ThrowIfNull(creatures);
var materials = new Dictionary<uint, string>();
uint masterDid = (uint)dats.Portal.Db.Header.MasterMapId;
EnumIDMap? master = masterDid == 0u
? null
: dats.Get<EnumIDMap>(masterDid);
if (master is not null
&& master.ClientEnumToID.TryGetValue(
MaterialClientEnum,
out uint materialRootMapDid)
&& dats.Get<EnumIDMap>(materialRootMapDid) is { } materialRootMap
&& materialRootMap.ClientEnumToID.TryGetValue(
MaterialSubEnum,
out uint materialMapDid)
&& dats.Get<DualEnumIDMap>(materialMapDid) is { } materialMap)
{
foreach ((uint id, PStringBase<byte> text)
in materialMap.ClientEnumToName)
{
materials.TryAdd(id, Normalize(text.Value));
}
}
return new RetailAppraisalNameResolver(materials, creatures);
}
public string ResolveCreature(int creatureType)
=> _creatures.Resolve(creatureType);
public string ResolveHeritage(int heritageGroup)
=> heritageGroup switch
{
2 => "Gharu'ndim",
5 => "Umbraen",
13 => "Olthoi",
_ => CharacterIdentityText.HeritageGroupDisplayName(heritageGroup)
?? string.Empty,
};
public string ResolveMaterial(int materialType)
=> materialType > 0
&& _materials.TryGetValue((uint)materialType, out string? name)
? name
: string.Empty;
private static string Normalize(string value)
=> value.Replace('_', ' ');
}

View file

@ -966,6 +966,7 @@ public sealed class RetailUiRuntime : IDisposable
ImportedLayout? layout;
CreatureAppraisalRowTemplateFactory? creatureRows;
CreatureDisplayNameResolver? creatureNames;
RetailAppraisalNameResolver? itemNames;
lock (_bindings.Assets.DatLock)
{
layout = LayoutImporter.Import(
@ -982,6 +983,9 @@ public sealed class RetailUiRuntime : IDisposable
_bindings.Assets.ResolveFont);
creatureNames = CreatureDisplayNameResolver.Load(
_bindings.Assets.Dats);
itemNames = RetailAppraisalNameResolver.Load(
_bindings.Assets.Dats,
creatureNames);
}
if (layout is null)
{
@ -1003,7 +1007,8 @@ public sealed class RetailUiRuntime : IDisposable
show: () => Host.ShowWindow(WindowNames.Examination),
close: () => CloseWindow(WindowNames.Examination),
creatureRowTemplates: creatureRows,
creatureNames: creatureNames);
creatureNames: creatureNames,
itemNames: itemNames);
if (controller is null)
{
Console.WriteLine(

View file

@ -23,6 +23,8 @@ public enum PublicWeenieFlags : uint
NonPlayerKillerSwitch = 0x00000800,
Door = 0x00001000,
Corpse = 0x00002000,
Healer = 0x00010000,
Lockpick = 0x00020000,
RequiresPackSlot = 0x00800000,
VolatileRare = 0x10000000,
WieldOnUse = 0x20000000,