feat(ui): complete retail creature appraisal details
Port the separate creature rating list, layer the animated preview between authored row chrome and text, and follow selection while the examination floaty is visible. Preserve the remaining item-preview and font-state gaps in AP-110. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
7eaa68a5f4
commit
d96ea2de98
11 changed files with 578 additions and 61 deletions
|
|
@ -4,6 +4,7 @@ using System.Text;
|
|||
using AcDream.Core.Combat;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Net.Messages;
|
||||
using AcDream.Core.Selection;
|
||||
using AcDream.Core.Spells;
|
||||
|
||||
namespace AcDream.App.UI.Layout;
|
||||
|
|
@ -29,6 +30,7 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
public const uint CreaturePanelId = 0x10000140u;
|
||||
public const uint CreatureViewportId = 0x10000148u;
|
||||
public const uint CreatureStatsListId = 0x10000149u;
|
||||
public const uint CreatureExtraListId = 0x10000335u;
|
||||
public const uint CreatureDisplayNameId = 0x1000014Eu;
|
||||
public const uint CreatureLevelValueId = 0x1000014Cu;
|
||||
public const uint SpellPanelId = 0x10000153u;
|
||||
|
|
@ -41,12 +43,14 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
private readonly ImportedLayout _layout;
|
||||
private readonly ClientObjectTable _objects;
|
||||
private readonly ItemInteractionController _interaction;
|
||||
private readonly SelectionState _selection;
|
||||
private readonly CombatState _combat;
|
||||
private readonly Spellbook _spellbook;
|
||||
private readonly Func<string> _playerName;
|
||||
private readonly Action<uint, string> _sendSetInscription;
|
||||
private readonly Action<string> _systemMessage;
|
||||
private readonly Action _show;
|
||||
private readonly Action _closeWindow;
|
||||
private readonly UiElement _itemPanel;
|
||||
private readonly UiElement _creaturePanel;
|
||||
private readonly UiElement? _spellPanel;
|
||||
|
|
@ -57,7 +61,8 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
private readonly UiText? _signature;
|
||||
private readonly UiDatElement? _inscriptionBackground;
|
||||
private readonly UiButton? _close;
|
||||
private readonly UiItemList? _creatureStats;
|
||||
private readonly CreatureAppraisalLayeredList? _creatureStats;
|
||||
private readonly CreatureAppraisalLayeredList? _creatureExtra;
|
||||
private readonly CreatureAppraisalRowTemplateFactory? _creatureRowTemplates;
|
||||
private readonly CreatureDisplayNameResolver _creatureNames;
|
||||
private AppraisalView _activeView;
|
||||
|
|
@ -71,6 +76,7 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
private string _scribeName = string.Empty;
|
||||
private string _oldInscription = string.Empty;
|
||||
private bool _presentationInscribable;
|
||||
private bool _windowVisible;
|
||||
private double _refreshElapsed;
|
||||
private bool _disposed;
|
||||
|
||||
|
|
@ -78,6 +84,7 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
ImportedLayout layout,
|
||||
ClientObjectTable objects,
|
||||
ItemInteractionController interaction,
|
||||
SelectionState selection,
|
||||
CombatState combat,
|
||||
Spellbook spellbook,
|
||||
Func<string> playerName,
|
||||
|
|
@ -95,12 +102,14 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
_layout = layout;
|
||||
_objects = objects;
|
||||
_interaction = interaction;
|
||||
_selection = selection;
|
||||
_combat = combat;
|
||||
_spellbook = spellbook;
|
||||
_playerName = playerName;
|
||||
_sendSetInscription = sendSetInscription;
|
||||
_systemMessage = systemMessage;
|
||||
_show = show;
|
||||
_closeWindow = close;
|
||||
_itemPanel = itemPanel;
|
||||
_creaturePanel = creaturePanel;
|
||||
_spellPanel = layout.FindElement(SpellPanelId);
|
||||
|
|
@ -150,24 +159,25 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
}
|
||||
|
||||
if (creatureRowTemplates is not null
|
||||
&& layout.FindElement(CreatureStatsListId) is { } statsHost)
|
||||
&& layout.FindElement(CreatureStatsListId) is { } statsHost
|
||||
&& layout.FindElement(CreatureExtraListId) is { } extraHost
|
||||
&& layout.FindElement(CreatureViewportId) is UiViewport viewport)
|
||||
{
|
||||
_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;
|
||||
_creatureStats = CreatureAppraisalLayeredList.Create(
|
||||
_creaturePanel,
|
||||
statsHost,
|
||||
viewport,
|
||||
creatureRowTemplates,
|
||||
backgroundZOrder: viewport.ZOrder - 2);
|
||||
_creatureExtra = CreatureAppraisalLayeredList.Create(
|
||||
_creaturePanel,
|
||||
extraHost,
|
||||
viewport,
|
||||
creatureRowTemplates,
|
||||
backgroundZOrder: viewport.ZOrder - 1);
|
||||
}
|
||||
|
||||
_selection.Changed += HandleSelectionChanged;
|
||||
SetActiveView(AppraisalView.Item);
|
||||
}
|
||||
|
||||
|
|
@ -178,6 +188,7 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
ImportedLayout layout,
|
||||
ClientObjectTable objects,
|
||||
ItemInteractionController interaction,
|
||||
SelectionState selection,
|
||||
CombatState combat,
|
||||
Spellbook spellbook,
|
||||
Func<string> playerName,
|
||||
|
|
@ -191,6 +202,7 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
ArgumentNullException.ThrowIfNull(layout);
|
||||
ArgumentNullException.ThrowIfNull(objects);
|
||||
ArgumentNullException.ThrowIfNull(interaction);
|
||||
ArgumentNullException.ThrowIfNull(selection);
|
||||
ArgumentNullException.ThrowIfNull(combat);
|
||||
ArgumentNullException.ThrowIfNull(spellbook);
|
||||
ArgumentNullException.ThrowIfNull(playerName);
|
||||
|
|
@ -209,6 +221,7 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
layout,
|
||||
objects,
|
||||
interaction,
|
||||
selection,
|
||||
combat,
|
||||
spellbook,
|
||||
playerName,
|
||||
|
|
@ -274,7 +287,7 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
|
||||
public void Tick(double deltaSeconds)
|
||||
{
|
||||
if (!_layout.Root.Visible
|
||||
if (!_windowVisible
|
||||
|| _activeView is not (AppraisalView.Creature or AppraisalView.Character)
|
||||
|| _combat.CurrentMode == CombatMode.NonCombat
|
||||
|| CurrentObjectId == 0)
|
||||
|
|
@ -522,18 +535,12 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
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));
|
||||
}
|
||||
}
|
||||
_creatureStats.Rebuild(
|
||||
appraisal.CreatureProfile is { } profile
|
||||
? CreatureAppraisalRows.Build(profile, appraisal.Success)
|
||||
: Array.Empty<CreatureAppraisalRow>());
|
||||
_creatureExtra?.Rebuild(
|
||||
CreatureAppraisalRows.BuildExtra(appraisal.Properties));
|
||||
}
|
||||
|
||||
private void ConfigureScrollableText(
|
||||
|
|
@ -577,11 +584,13 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
if (_layout.FindElement(id) is UiText text)
|
||||
text.LinesProvider = static () => Array.Empty<UiText.Line>();
|
||||
_creatureStats?.Flush();
|
||||
_creatureExtra?.Flush();
|
||||
}
|
||||
|
||||
private void ResetCreatureScroll()
|
||||
{
|
||||
_creatureStats?.Scroll.SetScrollY(0);
|
||||
_creatureStats?.ResetScroll();
|
||||
_creatureExtra?.ResetScroll();
|
||||
foreach (UiText text in Descendants(_creaturePanel).OfType<UiText>())
|
||||
text.Scroll.SetScrollY(0);
|
||||
}
|
||||
|
|
@ -647,6 +656,29 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
|
||||
public void OnShown()
|
||||
{
|
||||
_windowVisible = true;
|
||||
}
|
||||
|
||||
public void OnHidden() => _windowVisible = false;
|
||||
|
||||
/// <summary>
|
||||
/// Port of <c>gmExaminationUI::RecvNotice_SelectionChanged @ 0x004AB3D0</c>.
|
||||
/// Retail initializes <c>m_examineNewlySelectedItem</c> to one: while the
|
||||
/// floaty window is visible, a new selection is appraised immediately and
|
||||
/// clearing selection hides the window.
|
||||
/// </summary>
|
||||
private void HandleSelectionChanged(SelectionTransition transition)
|
||||
{
|
||||
if (!_windowVisible || _disposed)
|
||||
return;
|
||||
|
||||
if (transition.SelectedObjectId is uint objectId && objectId != 0u)
|
||||
{
|
||||
_interaction.ExamineSelectedOrEnterMode(objectId);
|
||||
return;
|
||||
}
|
||||
|
||||
_closeWindow();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
@ -654,6 +686,7 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
_selection.Changed -= HandleSelectionChanged;
|
||||
if (_close is not null)
|
||||
_close.OnClick = null;
|
||||
if (_inscriptionField is not null)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue