using System.Globalization;
namespace AcDream.App.UI.Layout;
///
/// Binds retail's SmartBox frame-rate display to the authored text element from
/// LayoutDesc 0x2100000F.
///
///
/// Faithful port of gmSmartBoxUI::RecvNotice_SetFramerateDisplay @ 0x004D65E0,
/// gmSmartBoxUI::UpdateFPSMeter @ 0x004D63A0, and
/// gmSmartBoxUI::UseTime @ 0x004D6E30. Portal string table 0x23000001,
/// string 0x0DCFFF73 supplies the two labels FPS: and DEG:;
/// retail inserts both floating-point values with two decimal places.
///
public sealed class RetailFpsController
{
public const uint LayoutId = 0x2100000Fu;
public const uint DisplayElementId = 0x10000047u;
private readonly UiText _display;
private readonly Func _framesPerSecond;
private readonly Func _degradeMultiplier;
private readonly Func _isVisible;
private RetailFpsController(
UiText display,
Func framesPerSecond,
Func degradeMultiplier,
Func isVisible)
{
_display = display;
_framesPerSecond = framesPerSecond;
_degradeMultiplier = degradeMultiplier;
_isVisible = isVisible;
// The selected catalog element is authored at (1,1) inside gmSmartBoxUI.
// Selective LayoutDesc import makes it a root, so restore that parent-relative
// placement explicitly instead of letting it drift to (0,0).
_display.Left = 1f;
_display.Top = 1f;
_display.Padding = 1f;
_display.Selectable = false;
_display.LinesProvider = BuildLines;
Tick();
}
public UiText Display => _display;
public static RetailFpsController? Bind(
ImportedLayout layout,
Func framesPerSecond,
Func degradeMultiplier,
Func isVisible)
{
ArgumentNullException.ThrowIfNull(layout);
ArgumentNullException.ThrowIfNull(framesPerSecond);
ArgumentNullException.ThrowIfNull(degradeMultiplier);
ArgumentNullException.ThrowIfNull(isVisible);
return layout.FindElement(DisplayElementId) is UiText display
? new RetailFpsController(display, framesPerSecond, degradeMultiplier, isVisible)
: null;
}
/// Mirrors the retail notice-driven show/hide state each UI tick.
public void Tick() => _display.Visible = _isVisible();
private IReadOnlyList BuildLines()
{
string fps = _framesPerSecond().ToString("F2", CultureInfo.InvariantCulture);
string degrade = _degradeMultiplier().ToString("F2", CultureInfo.InvariantCulture);
return
[
new UiText.Line($"FPS: {fps}", _display.DefaultColor),
new UiText.Line($"DEG: {degrade}", _display.DefaultColor),
];
}
}