fix(ui): match retail vitae and character info
Restore Vitae's omitted penalty paragraph, replace the invented character summary with gmCharacterInfoUI's ordered report and property meanings, preserve authored translucent body surfaces, and initialize the end-session button in its visible Normal DAT state. Release build and all 5,830 tests pass with five intentional skips. Connected visual gate pending. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
d1d603105f
commit
16c21e299c
21 changed files with 771 additions and 303 deletions
|
|
@ -919,6 +919,7 @@ public sealed class RetailUiRuntime : IDisposable
|
|||
ContentAnchors = AnchorEdges.Left | AnchorEdges.Top
|
||||
| AnchorEdges.Right | AnchorEdges.Bottom,
|
||||
ContentClickThrough = false,
|
||||
DrawChromeCenter = !AuthorsFullPanelCenter(rootInfo),
|
||||
Controller = controller,
|
||||
});
|
||||
_panelUi.RegisterMainPanel(
|
||||
|
|
@ -943,12 +944,14 @@ public sealed class RetailUiRuntime : IDisposable
|
|||
{
|
||||
ElementInfo? rootInfo;
|
||||
ImportedLayout? layout;
|
||||
CharacterInfoStrings strings;
|
||||
lock (_bindings.Assets.DatLock)
|
||||
{
|
||||
rootInfo = LayoutImporter.ImportInfos(
|
||||
_bindings.Assets.Dats,
|
||||
CharacterController.LayoutId,
|
||||
CharacterController.RootId);
|
||||
var resolver = new DatStringResolver(_bindings.Assets.Dats);
|
||||
layout = rootInfo is null
|
||||
? null
|
||||
: LayoutImporter.Build(
|
||||
|
|
@ -956,7 +959,9 @@ public sealed class RetailUiRuntime : IDisposable
|
|||
_bindings.Assets.ResolveSprite,
|
||||
_bindings.Assets.DefaultFont,
|
||||
_bindings.Assets.ResolveFont,
|
||||
new DatStringResolver(_bindings.Assets.Dats).Resolve);
|
||||
resolver.Resolve);
|
||||
strings = CharacterInfoStrings.FromDat(key => resolver.ResolveAll(
|
||||
0x23000001u, DatStringResolver.ComputeHash(key)));
|
||||
}
|
||||
if (rootInfo is null || layout is null) return;
|
||||
|
||||
|
|
@ -964,7 +969,8 @@ public sealed class RetailUiRuntime : IDisposable
|
|||
layout,
|
||||
_bindings.Character.Provider.BuildSheet,
|
||||
_bindings.Assets.DefaultFont,
|
||||
() => CloseWindow(WindowNames.CharacterInformation));
|
||||
() => CloseWindow(WindowNames.CharacterInformation),
|
||||
strings);
|
||||
RegisterIndicatorDetailPanel(
|
||||
RetailPanelCatalog.CharacterInformation,
|
||||
WindowNames.CharacterInformation,
|
||||
|
|
@ -1042,11 +1048,19 @@ public sealed class RetailUiRuntime : IDisposable
|
|||
resolver.Resolve);
|
||||
VitaeStrings fallback = VitaeStrings.English;
|
||||
strings = new VitaeStrings(
|
||||
resolver.Resolve(0x23000001u, 0x0142DDECu) ?? fallback.FullStrength,
|
||||
resolver.Resolve(0x23000001u, 0x045D7665u, 0) ?? fallback.LostPrefix,
|
||||
resolver.Resolve(0x23000001u, 0x045D7665u, 1) ?? fallback.LostSuffix,
|
||||
resolver.Resolve(0x23000001u, 0x0DCD8C35u, 0) ?? fallback.RecoveryPrefix,
|
||||
resolver.Resolve(0x23000001u, 0x0DCD8C35u, 1) ?? fallback.RecoverySuffix);
|
||||
Resolve("ID_Vitae_Text_Full", 0, fallback.FullStrength),
|
||||
Resolve("ID_Vitae_Text_Vitae", 0, fallback.LostPrefix),
|
||||
Resolve("ID_Vitae_Text_Vitae", 1, fallback.LostSuffix),
|
||||
Resolve("ID_Vitae_Text_Skills", 0, fallback.SkillsPrefix),
|
||||
Resolve("ID_Vitae_Text_Skills", 1, fallback.SkillsSuffix),
|
||||
Resolve("ID_Vitae_Text_Experience", 0, fallback.RecoveryPrefix),
|
||||
Resolve("ID_Vitae_Text_Experience", 1, fallback.RecoverySuffix));
|
||||
|
||||
string Resolve(string key, int token, string fallbackValue)
|
||||
=> resolver.Resolve(
|
||||
0x23000001u,
|
||||
DatStringResolver.ComputeHash(key),
|
||||
token) ?? fallbackValue;
|
||||
}
|
||||
if (rootInfo is null || layout is null) return;
|
||||
|
||||
|
|
@ -1127,6 +1141,7 @@ public sealed class RetailUiRuntime : IDisposable
|
|||
ContentAnchors = AnchorEdges.Left | AnchorEdges.Top
|
||||
| AnchorEdges.Right | AnchorEdges.Bottom,
|
||||
ContentClickThrough = false,
|
||||
DrawChromeCenter = !AuthorsFullPanelCenter(rootInfo),
|
||||
Controller = controller,
|
||||
});
|
||||
_panelUi.RegisterMainPanel(
|
||||
|
|
@ -1139,6 +1154,28 @@ public sealed class RetailUiRuntime : IDisposable
|
|||
&& restorePrevious);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retail's Vitae and Character Information children carry their own
|
||||
/// 0x06004CC2 body surface below the 25px title row. The standalone shared
|
||||
/// window contributes the border only in that case; painting the same
|
||||
/// translucent center twice makes the panel look opaque.
|
||||
/// </summary>
|
||||
internal static bool AuthorsFullPanelCenter(ElementInfo rootInfo)
|
||||
{
|
||||
const float RetailTitleHeight = 25f;
|
||||
return Visit(rootInfo);
|
||||
|
||||
bool Visit(ElementInfo info)
|
||||
{
|
||||
bool coversBody = info.Width >= rootInfo.Width
|
||||
&& info.Height >= rootInfo.Height - RetailTitleHeight;
|
||||
if (coversBody && info.StateMedia.Values.Any(
|
||||
media => media.File == RetailChromeSprites.CenterFill))
|
||||
return true;
|
||||
return info.Children.Any(Visit);
|
||||
}
|
||||
}
|
||||
|
||||
private void MountIndicators()
|
||||
{
|
||||
ImportedLayout? layout = Import(IndicatorBarController.LayoutId);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue