feat(ui): port retail creature appraisal presentation
Render assessed creatures through the shared private viewport with retail heading, bounding-box camera, and light. Build the exact authored nine-row stat list and resolve creature names from the retail EnumMapper while keeping remaining font/sequencer adaptations explicit. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
f1a7912160
commit
7eaa68a5f4
26 changed files with 2780 additions and 237 deletions
|
|
@ -27,6 +27,10 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
public const uint SignatureTextId = 0x1000013Fu;
|
||||
public const uint InscriptionScrollbarId = 0x1000046Eu;
|
||||
public const uint CreaturePanelId = 0x10000140u;
|
||||
public const uint CreatureViewportId = 0x10000148u;
|
||||
public const uint CreatureStatsListId = 0x10000149u;
|
||||
public const uint CreatureDisplayNameId = 0x1000014Eu;
|
||||
public const uint CreatureLevelValueId = 0x1000014Cu;
|
||||
public const uint SpellPanelId = 0x10000153u;
|
||||
|
||||
private const uint TemplateStringProperty = 5u;
|
||||
|
|
@ -53,6 +57,9 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
private readonly UiText? _signature;
|
||||
private readonly UiDatElement? _inscriptionBackground;
|
||||
private readonly UiButton? _close;
|
||||
private readonly UiItemList? _creatureStats;
|
||||
private readonly CreatureAppraisalRowTemplateFactory? _creatureRowTemplates;
|
||||
private readonly CreatureDisplayNameResolver _creatureNames;
|
||||
private AppraisalView _activeView;
|
||||
private uint _itemObjectId;
|
||||
private uint _creatureObjectId;
|
||||
|
|
@ -81,7 +88,9 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
UiElement itemPanel,
|
||||
UiElement creaturePanel,
|
||||
UiText title,
|
||||
UiText itemText)
|
||||
UiText itemText,
|
||||
CreatureAppraisalRowTemplateFactory? creatureRowTemplates,
|
||||
CreatureDisplayNameResolver? creatureNames)
|
||||
{
|
||||
_layout = layout;
|
||||
_objects = objects;
|
||||
|
|
@ -97,6 +106,10 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
_spellPanel = layout.FindElement(SpellPanelId);
|
||||
_title = title;
|
||||
_itemText = itemText;
|
||||
_creatureRowTemplates = creatureRowTemplates;
|
||||
_creatureNames = creatureNames
|
||||
?? new CreatureDisplayNameResolver(
|
||||
new Dictionary<uint, string>());
|
||||
_inscriptionText = layout.FindElement(InscriptionTextId) as UiText;
|
||||
_inscriptionField = layout.FindElement(InscriptionTextId) as UiField;
|
||||
_signature = layout.FindElement(SignatureTextId) as UiText;
|
||||
|
|
@ -136,6 +149,25 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
_inscriptionBackground.ClickThrough = false;
|
||||
}
|
||||
|
||||
if (creatureRowTemplates is not null
|
||||
&& layout.FindElement(CreatureStatsListId) is { } statsHost)
|
||||
{
|
||||
_creatureStats = statsHost as UiItemList
|
||||
?? new UiItemList
|
||||
{
|
||||
Width = statsHost.Width,
|
||||
Height = statsHost.Height,
|
||||
Anchors = AnchorEdges.Left | AnchorEdges.Top
|
||||
| AnchorEdges.Right | AnchorEdges.Bottom,
|
||||
};
|
||||
if (!ReferenceEquals(_creatureStats, statsHost))
|
||||
statsHost.AddChild(_creatureStats);
|
||||
_creatureStats.Flush();
|
||||
_creatureStats.Columns = 1;
|
||||
_creatureStats.CellWidth = creatureRowTemplates.Width;
|
||||
_creatureStats.CellHeight = creatureRowTemplates.Height;
|
||||
}
|
||||
|
||||
SetActiveView(AppraisalView.Item);
|
||||
}
|
||||
|
||||
|
|
@ -152,7 +184,9 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
Action<uint, string> sendSetInscription,
|
||||
Action<string> systemMessage,
|
||||
Action show,
|
||||
Action close)
|
||||
Action close,
|
||||
CreatureAppraisalRowTemplateFactory? creatureRowTemplates = null,
|
||||
CreatureDisplayNameResolver? creatureNames = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(layout);
|
||||
ArgumentNullException.ThrowIfNull(objects);
|
||||
|
|
@ -185,7 +219,9 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
itemPanel,
|
||||
creaturePanel,
|
||||
title,
|
||||
itemText);
|
||||
itemText,
|
||||
creatureRowTemplates,
|
||||
creatureNames);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -452,17 +488,12 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
{
|
||||
ClearCreatureText();
|
||||
PropertyBundle p = appraisal.Properties;
|
||||
SetText(0x1000014Au, character
|
||||
? GetString(p, 4u)
|
||||
: AppraisalTextFormatter.CreatureTypeName(GetInt(p, 2u)));
|
||||
SetText(0x1000014Bu, GetString(p, 2u));
|
||||
SetText(0x1000014Cu, GetString(p, 14u));
|
||||
|
||||
string report = AppraisalTextFormatter.BuildCreature(
|
||||
obj,
|
||||
appraisal,
|
||||
character);
|
||||
SetText(0x1000014Eu, report, scrollable: true);
|
||||
int level = GetInt(p, 25u);
|
||||
SetText(
|
||||
CreatureLevelValueId,
|
||||
level > 0
|
||||
? level.ToString(CultureInfo.CurrentCulture)
|
||||
: "???");
|
||||
|
||||
if (character)
|
||||
{
|
||||
|
|
@ -470,6 +501,14 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
SetText(0x10000151u, GetString(p, 47u));
|
||||
SetText(0x10000152u, GetString(p, 11u));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetText(
|
||||
CreatureDisplayNameId,
|
||||
_creatureNames.Resolve(GetInt(p, 2u)));
|
||||
}
|
||||
|
||||
RebuildCreatureStats(appraisal);
|
||||
|
||||
SetText(0x1000053Au, appraisal.Success
|
||||
? string.Empty
|
||||
|
|
@ -478,6 +517,25 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
ResetCreatureScroll();
|
||||
}
|
||||
|
||||
private void RebuildCreatureStats(AppraiseInfoParser.Parsed appraisal)
|
||||
{
|
||||
if (_creatureStats is null || _creatureRowTemplates is null)
|
||||
return;
|
||||
|
||||
using (_creatureStats.DeferLayout())
|
||||
{
|
||||
_creatureStats.Flush();
|
||||
if (appraisal.CreatureProfile is not { } profile)
|
||||
return;
|
||||
foreach (CreatureAppraisalRow row in CreatureAppraisalRows.Build(
|
||||
profile,
|
||||
appraisal.Success))
|
||||
{
|
||||
_creatureStats.AddItem(_creatureRowTemplates.Create(row));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfigureScrollableText(
|
||||
UiText text,
|
||||
uint scrollbarId,
|
||||
|
|
@ -513,15 +571,17 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
{
|
||||
foreach (uint id in new uint[]
|
||||
{
|
||||
0x1000014Au, 0x1000014Bu, 0x1000014Cu, 0x1000014Eu,
|
||||
CreatureLevelValueId, CreatureDisplayNameId,
|
||||
0x10000150u, 0x10000151u, 0x10000152u, 0x1000053Au,
|
||||
})
|
||||
if (_layout.FindElement(id) is UiText text)
|
||||
text.LinesProvider = static () => Array.Empty<UiText.Line>();
|
||||
_creatureStats?.Flush();
|
||||
}
|
||||
|
||||
private void ResetCreatureScroll()
|
||||
{
|
||||
_creatureStats?.Scroll.SetScrollY(0);
|
||||
foreach (UiText text in Descendants(_creaturePanel).OfType<UiText>())
|
||||
text.Scroll.SetScrollY(0);
|
||||
}
|
||||
|
|
|
|||
304
src/AcDream.App/UI/Layout/CreatureAppraisalRows.cs
Normal file
304
src/AcDream.App/UI/Layout/CreatureAppraisalRows.cs
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
using System.Globalization;
|
||||
using System.Numerics;
|
||||
using AcDream.Content;
|
||||
using AcDream.Core.Net.Messages;
|
||||
using DatReaderWriter;
|
||||
using DatReaderWriter.DBObjs;
|
||||
using DatReaderWriter.Types;
|
||||
|
||||
namespace AcDream.App.UI.Layout;
|
||||
|
||||
public enum CreatureAppraisalValueStyle
|
||||
{
|
||||
Normal,
|
||||
Positive,
|
||||
Negative,
|
||||
Incomplete,
|
||||
}
|
||||
|
||||
public readonly record struct CreatureAppraisalRow(
|
||||
string Label,
|
||||
string Value,
|
||||
CreatureAppraisalValueStyle Style);
|
||||
|
||||
/// <summary>
|
||||
/// Pure projection of retail's six <c>AttributeInfoRegion</c> and three
|
||||
/// <c>Attribute2ndInfoRegion</c> tokens. Ordering and formatting come from
|
||||
/// <c>BasicCreatureExamineUI</c> and the two Update overloads at
|
||||
/// 0x004F1D90/0x004F1E80.
|
||||
/// </summary>
|
||||
public static class CreatureAppraisalRows
|
||||
{
|
||||
private const string Unknown = "???";
|
||||
|
||||
public static IReadOnlyList<CreatureAppraisalRow> Build(
|
||||
AppraiseInfoParser.CreatureProfile profile,
|
||||
bool success)
|
||||
{
|
||||
return
|
||||
[
|
||||
Primary("Strength", profile.Strength, 0, profile, success),
|
||||
Primary("Endurance", profile.Endurance, 1, profile, success),
|
||||
Primary("Coordination", profile.Coordination, 3, profile, success),
|
||||
Primary("Quickness", profile.Quickness, 2, profile, success),
|
||||
Primary("Focus", profile.Focus, 4, profile, success),
|
||||
Primary("Self", profile.Self, 5, profile, success),
|
||||
Secondary(
|
||||
"Health",
|
||||
profile.Health,
|
||||
profile.HealthMax,
|
||||
showPercent: true,
|
||||
enchantmentBit: 6,
|
||||
profile,
|
||||
success),
|
||||
Secondary(
|
||||
"Stamina",
|
||||
profile.Stamina,
|
||||
profile.StaminaMax,
|
||||
showPercent: false,
|
||||
enchantmentBit: 7,
|
||||
profile,
|
||||
success),
|
||||
Secondary(
|
||||
"Mana",
|
||||
profile.Mana,
|
||||
profile.ManaMax,
|
||||
showPercent: false,
|
||||
enchantmentBit: 8,
|
||||
profile,
|
||||
success),
|
||||
];
|
||||
}
|
||||
|
||||
private static CreatureAppraisalRow Primary(
|
||||
string label,
|
||||
uint? value,
|
||||
int enchantmentBit,
|
||||
AppraiseInfoParser.CreatureProfile profile,
|
||||
bool success)
|
||||
=> new(
|
||||
label,
|
||||
value is > 0
|
||||
? value.Value.ToString(CultureInfo.CurrentCulture)
|
||||
: Unknown,
|
||||
Style(enchantmentBit, profile, success));
|
||||
|
||||
private static CreatureAppraisalRow Secondary(
|
||||
string label,
|
||||
uint? current,
|
||||
uint? maximum,
|
||||
bool showPercent,
|
||||
int enchantmentBit,
|
||||
AppraiseInfoParser.CreatureProfile profile,
|
||||
bool success)
|
||||
{
|
||||
string value = Unknown;
|
||||
if (current is > 0 && maximum is > 0)
|
||||
{
|
||||
int percent = RoundedPercent(current.Value, maximum.Value);
|
||||
if (success)
|
||||
{
|
||||
value = showPercent
|
||||
? $"{current.Value.ToString(CultureInfo.CurrentCulture)}/{maximum.Value.ToString(CultureInfo.CurrentCulture)} ({percent.ToString(CultureInfo.CurrentCulture)} %)"
|
||||
: $"{current.Value.ToString(CultureInfo.CurrentCulture)}/{maximum.Value.ToString(CultureInfo.CurrentCulture)}";
|
||||
}
|
||||
else if (showPercent)
|
||||
{
|
||||
value = $"{percent.ToString(CultureInfo.CurrentCulture)} %";
|
||||
}
|
||||
}
|
||||
|
||||
return new CreatureAppraisalRow(
|
||||
label,
|
||||
value,
|
||||
Style(enchantmentBit, profile, success));
|
||||
}
|
||||
|
||||
private static int RoundedPercent(uint numerator, uint denominator)
|
||||
=> denominator == 0u
|
||||
? 0
|
||||
: (int)Math.Min(
|
||||
int.MaxValue,
|
||||
((100L * numerator) + denominator / 2L) / denominator);
|
||||
|
||||
private static CreatureAppraisalValueStyle Style(
|
||||
int bit,
|
||||
AppraiseInfoParser.CreatureProfile profile,
|
||||
bool success)
|
||||
{
|
||||
if (!success)
|
||||
return CreatureAppraisalValueStyle.Incomplete;
|
||||
|
||||
ushort highlight = profile.AttributeHighlights ?? 0;
|
||||
if ((highlight & (1 << bit)) == 0)
|
||||
return CreatureAppraisalValueStyle.Normal;
|
||||
|
||||
ushort color = profile.AttributeColors ?? 0;
|
||||
return (color & (1 << bit)) != 0
|
||||
? CreatureAppraisalValueStyle.Positive
|
||||
: CreatureAppraisalValueStyle.Negative;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates LayoutDesc 0x2100006B's InfoRegion token template
|
||||
/// 0x10000166, matching <c>UIElement_ListBox::AddItemFromTemplateList</c>.
|
||||
/// </summary>
|
||||
public sealed class CreatureAppraisalRowTemplateFactory
|
||||
{
|
||||
public const uint TemplateId = 0x10000166u;
|
||||
public const uint LabelId = 0x1000012Au;
|
||||
public const uint ValueId = 0x1000012Bu;
|
||||
|
||||
private readonly ElementInfo _template;
|
||||
private readonly Func<uint, (uint Texture, int Width, int Height)> _resolveSprite;
|
||||
private readonly UiDatFont? _defaultFont;
|
||||
private readonly IReadOnlyDictionary<uint, UiDatFont?> _fonts;
|
||||
|
||||
public CreatureAppraisalRowTemplateFactory(
|
||||
ElementInfo template,
|
||||
Func<uint, (uint Texture, int Width, int Height)> resolveSprite,
|
||||
UiDatFont? defaultFont,
|
||||
IReadOnlyDictionary<uint, UiDatFont?>? fonts = null)
|
||||
{
|
||||
_template = template ?? throw new ArgumentNullException(nameof(template));
|
||||
_resolveSprite = resolveSprite ?? throw new ArgumentNullException(nameof(resolveSprite));
|
||||
_defaultFont = defaultFont;
|
||||
_fonts = fonts ?? new Dictionary<uint, UiDatFont?>();
|
||||
}
|
||||
|
||||
public float Width => _template.Width;
|
||||
public float Height => _template.Height;
|
||||
|
||||
public static CreatureAppraisalRowTemplateFactory? TryLoad(
|
||||
IDatReaderWriter dats,
|
||||
Func<uint, (uint Texture, int Width, int Height)> resolveSprite,
|
||||
UiDatFont? defaultFont,
|
||||
Func<uint, UiDatFont?>? resolveFont)
|
||||
{
|
||||
ElementInfo? template = LayoutImporter.ImportInfos(
|
||||
dats,
|
||||
AppraisalUiController.LayoutId,
|
||||
TemplateId);
|
||||
if (template is null
|
||||
|| Find(template, LabelId) is null
|
||||
|| Find(template, ValueId) is null
|
||||
|| template.Width <= 0f
|
||||
|| template.Height <= 0f)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var fonts = new Dictionary<uint, UiDatFont?>();
|
||||
CaptureFonts(template, resolveFont, fonts);
|
||||
return new CreatureAppraisalRowTemplateFactory(
|
||||
template,
|
||||
resolveSprite,
|
||||
defaultFont,
|
||||
fonts);
|
||||
}
|
||||
|
||||
public UiTemplateListSlot Create(CreatureAppraisalRow row)
|
||||
{
|
||||
ImportedLayout content = LayoutImporter.Build(
|
||||
_template,
|
||||
_resolveSprite,
|
||||
_defaultFont,
|
||||
did => _fonts.TryGetValue(did, out UiDatFont? font)
|
||||
? font
|
||||
: _defaultFont);
|
||||
UiText label = Required<UiText>(content, LabelId);
|
||||
UiText value = Required<UiText>(content, ValueId);
|
||||
label.LinesProvider = () =>
|
||||
[new UiText.Line(row.Label, label.DefaultColor)];
|
||||
value.LinesProvider = () =>
|
||||
[new UiText.Line(row.Value, ResolveColor(row.Style, value.DefaultColor))];
|
||||
|
||||
return new UiTemplateListSlot(
|
||||
content,
|
||||
entryId: 0u,
|
||||
content.Root is IUiDatStateful stateful
|
||||
? stateful.ActiveRetailStateId
|
||||
: UiStateInfo.DirectStateId,
|
||||
selectedState: null);
|
||||
}
|
||||
|
||||
private static Vector4 ResolveColor(
|
||||
CreatureAppraisalValueStyle style,
|
||||
Vector4 normal)
|
||||
{
|
||||
// Retail selects one of four authored FontInfo entries here. Keep the
|
||||
// semantic state in the row model, but do not invent substitute RGB
|
||||
// values while that FontInfo-list binding remains unported (AP-110).
|
||||
_ = style;
|
||||
return normal;
|
||||
}
|
||||
|
||||
private static T Required<T>(ImportedLayout content, uint id)
|
||||
where T : UiElement
|
||||
=> content.FindElement(id) as T
|
||||
?? throw new InvalidOperationException(
|
||||
$"Retail creature appraisal template element 0x{id:X8} did not resolve to {typeof(T).Name}.");
|
||||
|
||||
private static ElementInfo? Find(ElementInfo root, uint id)
|
||||
{
|
||||
if (root.Id == id)
|
||||
return root;
|
||||
foreach (ElementInfo child in root.Children)
|
||||
if (Find(child, id) is { } found)
|
||||
return found;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void CaptureFonts(
|
||||
ElementInfo info,
|
||||
Func<uint, UiDatFont?>? resolveFont,
|
||||
Dictionary<uint, UiDatFont?> fonts)
|
||||
{
|
||||
if (info.FontDid != 0u && !fonts.ContainsKey(info.FontDid))
|
||||
fonts[info.FontDid] = resolveFont?.Invoke(info.FontDid);
|
||||
foreach (ElementInfo child in info.Children)
|
||||
CaptureFonts(child, resolveFont, fonts);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retail <c>AppraisalSystem::InqCreatureDisplayName @ 0x005B59E0</c> resolves
|
||||
/// creature enum 0x10000005 through portal EnumMapper 0x2200000E and replaces
|
||||
/// underscores with spaces.
|
||||
/// </summary>
|
||||
public sealed class CreatureDisplayNameResolver
|
||||
{
|
||||
public const uint MapperDid = 0x2200000Eu;
|
||||
private readonly IReadOnlyDictionary<uint, string> _names;
|
||||
|
||||
public CreatureDisplayNameResolver(
|
||||
IReadOnlyDictionary<uint, string> names)
|
||||
{
|
||||
_names = names ?? throw new ArgumentNullException(nameof(names));
|
||||
}
|
||||
|
||||
public static CreatureDisplayNameResolver Load(IDatReaderWriter dats)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(dats);
|
||||
var names = new Dictionary<uint, string>();
|
||||
var visited = new HashSet<uint>();
|
||||
uint did = MapperDid;
|
||||
while (did != 0u && visited.Add(did))
|
||||
{
|
||||
EnumMapper? mapper = dats.Get<EnumMapper>(did);
|
||||
if (mapper is null)
|
||||
break;
|
||||
foreach ((uint id, PStringBase<byte> text) in mapper.IdToStringMap)
|
||||
names.TryAdd(id, text.Value.Replace('_', ' '));
|
||||
did = mapper.BaseEnumMap;
|
||||
}
|
||||
return new CreatureDisplayNameResolver(names);
|
||||
}
|
||||
|
||||
public string Resolve(int creatureType)
|
||||
=> creatureType > 0
|
||||
&& _names.TryGetValue((uint)creatureType, out string? name)
|
||||
? name
|
||||
: string.Empty;
|
||||
}
|
||||
|
|
@ -301,6 +301,8 @@ public sealed class RetailUiRuntime : IDisposable
|
|||
public SpellcastingUiController? SpellcastingUiController { get; private set; }
|
||||
public SpellbookWindowController? SpellbookWindowController { get; private set; }
|
||||
public AppraisalUiController? AppraisalController { get; private set; }
|
||||
public UiViewport? CreatureAppraisalViewportWidget { get; private set; }
|
||||
public UiElement? ExaminationFrame { get; private set; }
|
||||
public EffectsUiController? PositiveEffectsController { get; private set; }
|
||||
public EffectsUiController? NegativeEffectsController { get; private set; }
|
||||
public LinkStatusUiController? LinkStatusUiController { get; private set; }
|
||||
|
|
@ -961,9 +963,26 @@ public sealed class RetailUiRuntime : IDisposable
|
|||
|
||||
private void MountAppraisal()
|
||||
{
|
||||
ImportedLayout? layout = Import(
|
||||
AppraisalUiController.LayoutId,
|
||||
AppraisalUiController.RootId);
|
||||
ImportedLayout? layout;
|
||||
CreatureAppraisalRowTemplateFactory? creatureRows;
|
||||
CreatureDisplayNameResolver? creatureNames;
|
||||
lock (_bindings.Assets.DatLock)
|
||||
{
|
||||
layout = LayoutImporter.Import(
|
||||
_bindings.Assets.Dats,
|
||||
AppraisalUiController.LayoutId,
|
||||
AppraisalUiController.RootId,
|
||||
_bindings.Assets.ResolveSprite,
|
||||
_bindings.Assets.DefaultFont,
|
||||
_bindings.Assets.ResolveFont);
|
||||
creatureRows = CreatureAppraisalRowTemplateFactory.TryLoad(
|
||||
_bindings.Assets.Dats,
|
||||
_bindings.Assets.ResolveSprite,
|
||||
_bindings.Assets.DefaultFont,
|
||||
_bindings.Assets.ResolveFont);
|
||||
creatureNames = CreatureDisplayNameResolver.Load(
|
||||
_bindings.Assets.Dats);
|
||||
}
|
||||
if (layout is null)
|
||||
{
|
||||
Console.WriteLine(
|
||||
|
|
@ -981,7 +1000,9 @@ public sealed class RetailUiRuntime : IDisposable
|
|||
_bindings.Appraisal.SendSetInscription,
|
||||
_bindings.Appraisal.DisplaySystemMessage,
|
||||
show: () => Host.ShowWindow(WindowNames.Examination),
|
||||
close: () => CloseWindow(WindowNames.Examination));
|
||||
close: () => CloseWindow(WindowNames.Examination),
|
||||
creatureRowTemplates: creatureRows,
|
||||
creatureNames: creatureNames);
|
||||
if (controller is null)
|
||||
{
|
||||
Console.WriteLine(
|
||||
|
|
@ -991,7 +1012,7 @@ public sealed class RetailUiRuntime : IDisposable
|
|||
|
||||
AppraisalController = controller;
|
||||
UiElement root = layout.Root;
|
||||
_ = RetailWindowFrame.Mount(
|
||||
RetailWindowHandle handle = RetailWindowFrame.Mount(
|
||||
Host.Root,
|
||||
root,
|
||||
_bindings.Assets.ResolveSprite,
|
||||
|
|
@ -1013,6 +1034,10 @@ public sealed class RetailUiRuntime : IDisposable
|
|||
ContentClickThrough = false,
|
||||
Controller = controller,
|
||||
});
|
||||
CreatureAppraisalViewportWidget =
|
||||
layout.FindElement(AppraisalUiController.CreatureViewportId)
|
||||
as UiViewport;
|
||||
ExaminationFrame = handle.OuterFrame;
|
||||
Console.WriteLine(
|
||||
"[M4] retail examination window from LayoutDesc 0x2100006B.");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue