fix #213: complete suicide and framerate presentation

Translate suicide response 0x004A as retail's successful self-kill notice. Port /framerate onto the authored SmartBox FPS element with live two-decimal FPS and DEG values, keep diagnostic window chrome independent, and synchronize command-driven settings without discarding panel drafts.

Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-13 15:50:47 +02:00
parent 9ea579bdd0
commit 43f7c7807c
17 changed files with 729 additions and 39 deletions

View file

@ -45,6 +45,11 @@ public sealed record CombatRuntimeBindings(
public sealed record JumpPowerbarRuntimeBindings(Func<JumpChargeSnapshot> Snapshot);
public sealed record FpsRuntimeBindings(
Func<double> FramesPerSecond,
Func<double> DegradeMultiplier,
Func<bool> IsVisible);
public sealed record ToolbarRuntimeBindings(
ClientObjectTable Objects,
Func<IReadOnlyList<ShortcutEntry>> Shortcuts,
@ -110,6 +115,7 @@ public sealed record RetailUiRuntimeBindings(
RadarRuntimeBindings Radar,
CombatRuntimeBindings Combat,
JumpPowerbarRuntimeBindings JumpPowerbar,
FpsRuntimeBindings Fps,
ToolbarRuntimeBindings Toolbar,
CharacterRuntimeBindings Character,
InventoryRuntimeBindings Inventory,
@ -135,6 +141,7 @@ public sealed class RetailUiRuntime : IDisposable
private RetailUiRuntime(RetailUiRuntimeBindings bindings)
{
_bindings = bindings;
MountFpsDisplay();
MountVitals();
MountRadar();
MountChat();
@ -180,6 +187,7 @@ public sealed class RetailUiRuntime : IDisposable
public ToolbarInputController? ToolbarInputController { get; private set; }
public CombatUiController? CombatUiController { get; private set; }
public JumpPowerbarController? JumpPowerbarController { get; private set; }
public RetailFpsController? FpsController { get; private set; }
public SelectedObjectController? SelectedObjectController { get; private set; }
public UiViewport? PaperdollViewportWidget { get; private set; }
public UiNineSlicePanel? InventoryFrame { get; private set; }
@ -201,6 +209,7 @@ public sealed class RetailUiRuntime : IDisposable
public void Tick(double deltaSeconds)
{
FpsController?.Tick();
JumpPowerbarController?.Tick();
SelectedObjectController?.Tick(deltaSeconds);
ConfirmationDialogs?.Tick();
@ -269,6 +278,47 @@ public sealed class RetailUiRuntime : IDisposable
_bindings.Assets.ResolveFont);
}
private ImportedLayout? Import(uint layoutId, uint rootElementId)
{
lock (_bindings.Assets.DatLock)
return LayoutImporter.Import(
_bindings.Assets.Dats,
layoutId,
rootElementId,
_bindings.Assets.ResolveSprite,
_bindings.Assets.DefaultFont,
_bindings.Assets.ResolveFont);
}
private void MountFpsDisplay()
{
ImportedLayout? layout = Import(
RetailFpsController.LayoutId,
RetailFpsController.DisplayElementId);
if (layout is null)
{
Console.WriteLine("[D.2b] FPS display: SmartBox element 0x10000047 not found.");
return;
}
FpsRuntimeBindings b = _bindings.Fps;
FpsController = RetailFpsController.Bind(
layout,
b.FramesPerSecond,
b.DegradeMultiplier,
b.IsVisible);
if (FpsController is null)
{
Console.WriteLine("[D.2b] FPS display: SmartBox element is not UIElement_Text.");
return;
}
// SmartBox HUD content is mounted before movable windows, matching retail's
// screen-layer ordering and ensuring ordinary panels can cover the readout.
Host.Root.AddChild(layout.Root);
Console.WriteLine("[D.2b] retail FPS display from SmartBox LayoutDesc 0x2100000F.");
}
private void MountVitals()
{
ImportedLayout? layout = Import(0x2100006Cu);