fix(chargen): Campaign CC gate round 1 closeout — Group 3: round review fixes (F4-F11, F14, F16)
The remaining code-bearing findings from the round review, F4-F16 minus the doc-only items (batched separately): - F4: three client-wide UiButton corpus sweeps (LabelBox path — exactly the 4 Town buttons, confined to chargen; conflicting custom-selection- pair + standard Normal/Highlight media — zero found, no gate tightening needed; per-state label-color map — 209 matches beyond chargen, confirming AP-222's mechanism has always been broadly active since it shipped generically in DatWidgetFactory). - F5/F6: LayoutImporter's Batch C un-consumed-children carve-out now honors a child's own AuthoredInvisible flag (a narrow honor scoped to exactly that carve-out, not the general #408 client-wide one) — the chat transcript's new-text indicator (0x1000048C) was building as a visible phantom element retail never shows; verified both directions against the gold-frame pieces, which do not author Invisible. - F7: BoundedProcessOutputCapture.AppendLine combines the line text and its trailing newline into one buffer and one file open/write/close instead of two. - F9: corrected a stale comment in RuntimeSettingsTargets — #407 split DisplayModeCatalog's Resolutions/WindowedResolutions in two, so the fullscreen validator's own narrower list is now DELIBERATELY different from the Config dropdown's fuller offering, not the "must match" bug the comment described. - F10: documented (not changed) why the LabelBox path's default 3px inset and the face-relative +4px gap in DatWidgetFactory.BuildButton are deliberately different numbers — neither carries a retail citation, and moving either to match the other would be an unfounded guess on a button that currently works correctly. - F11: Heritage/Profession/Summary/Town description pages now compose DatRichText.Compose's result ONCE inside their already revision-gated Refresh, caching the built line list instead of re-wrapping on every draw call. - F14: documented (not changed) why PrivateEntityViewportRenderer's _animatedIds set carrying a reserved-but-never-drawn backdrop id is harmless — BuildDrawEntities already excludes a null/empty backdrop from the actual draw list, so the id is never looked up. - F16: the Summary preview now uses its own render-id pair (SummaryPreviewRenderId/SummaryPreviewBackdropRenderId, 0xDA11D035/ 0xDA11D036) instead of sharing the Appearance page's (0xDA11D032/0xDA11D034) — confirmed by tracing FixedEntityTextureOwnerLease through TextureCache to CompositeTextureArrayCache's shared owner tracker that both pages' previews share ONE process-wide TextureCache, so sharing render ids was a real cross-page texture-release collision (either page's own re-dress or disposal could release the OTHER page's still-active textures), not a theoretical one. F3's own register bookkeeping (AP-229 addendum) and F12's register/AD header-count corrections land in the docs-only commit alongside F15. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
0fed5fdd91
commit
bd359d5181
17 changed files with 636 additions and 37 deletions
|
|
@ -174,7 +174,13 @@ internal sealed class CharacterCreationHeritagePage : IDisposable
|
|||
|
||||
IReadOnlyList<DatRichText.Segment> segments = ComposeSegments(
|
||||
_description, view, snapshot.HeritageId, _bindings.ResolveText);
|
||||
_description.LinesProvider = () => DatRichText.Compose(_description, segments);
|
||||
// F11 (Campaign CC gate round 1 closeout): compose ONCE here, inside
|
||||
// Refresh (already revision-gated by CharacterCreationUiController.Tick
|
||||
// — this method only runs when something in chargen state actually
|
||||
// changed), and hand LinesProvider the already-built list instead of
|
||||
// re-composing (escape-normalize + word-wrap) on EVERY draw call.
|
||||
IReadOnlyList<UiText.Line> composed = DatRichText.Compose(_description, segments);
|
||||
_description.LinesProvider = () => composed;
|
||||
}
|
||||
|
||||
internal void Randomize(RuntimeCharacterCreationSnapshot snapshot)
|
||||
|
|
|
|||
|
|
@ -256,7 +256,12 @@ internal sealed class CharacterCreationProfessionPage : IDisposable
|
|||
{
|
||||
string? text = _bindings.ResolveText?.Invoke(key);
|
||||
var segments = new[] { new DatRichText.Segment(text, _description.DefaultColor) };
|
||||
_description.LinesProvider = () => DatRichText.Compose(_description, segments);
|
||||
// F11 (Campaign CC gate round 1 closeout): compose ONCE here
|
||||
// (Refresh is already revision-gated) instead of re-wrapping on
|
||||
// every draw call — see CharacterCreationHeritagePage.Refresh's
|
||||
// own comment for the full rationale.
|
||||
IReadOnlyList<UiText.Line> composed = DatRichText.Compose(_description, segments);
|
||||
_description.LinesProvider = () => composed;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -286,9 +286,14 @@ internal sealed class CharacterCreationSummaryPage : IDisposable
|
|||
if (builder.Length == 0)
|
||||
return;
|
||||
|
||||
string composed = builder.ToString();
|
||||
var segments = new[] { new DatRichText.Segment(composed, _howToText.DefaultColor) };
|
||||
_howToText.LinesProvider = () => DatRichText.Compose(_howToText, segments);
|
||||
string composedText = builder.ToString();
|
||||
var segments = new[] { new DatRichText.Segment(composedText, _howToText.DefaultColor) };
|
||||
// F11 (Campaign CC gate round 1 closeout): compose ONCE here
|
||||
// (Refresh is already revision-gated) instead of re-wrapping on
|
||||
// every draw call — see CharacterCreationHeritagePage.Refresh's own
|
||||
// comment for the full rationale.
|
||||
IReadOnlyList<UiText.Line> composedLines = DatRichText.Compose(_howToText, segments);
|
||||
_howToText.LinesProvider = () => composedLines;
|
||||
}
|
||||
|
||||
// ── Name field (ListenToElementMessage @ 0x0047bf40) ────────────────
|
||||
|
|
|
|||
|
|
@ -118,7 +118,12 @@ internal sealed class CharacterCreationTownPage : IDisposable
|
|||
// looked like the text never changed).
|
||||
string composed = ComposeDescription(snapshot.StartArea, _bindings.ResolveText);
|
||||
var segments = new[] { new DatRichText.Segment(composed, _description.DefaultColor) };
|
||||
_description.LinesProvider = () => DatRichText.Compose(_description, segments);
|
||||
// F11 (Campaign CC gate round 1 closeout): compose ONCE here
|
||||
// (Refresh is already revision-gated) instead of re-wrapping on
|
||||
// every draw call — see CharacterCreationHeritagePage.Refresh's own
|
||||
// comment for the full rationale.
|
||||
IReadOnlyList<UiText.Line> composedLines = DatRichText.Compose(_description, segments);
|
||||
_description.LinesProvider = () => composedLines;
|
||||
}
|
||||
|
||||
internal void Randomize(IRuntimeCharacterCreationView view)
|
||||
|
|
|
|||
|
|
@ -915,6 +915,22 @@ public static class DatWidgetFactory
|
|||
else
|
||||
{
|
||||
button.LabelAlign = UiButton.LabelAlignment.Left;
|
||||
// F10 (Campaign CC gate round 1 closeout): this +4f gap and
|
||||
// UiButton.LabelOffsetX's own class-default 3f (used by the
|
||||
// "no face, not lifted" branch below, AND by any caller —
|
||||
// e.g. PaperdollController's "Slots" label — that sets
|
||||
// LabelAlign=Left directly with no DatWidgetFactory
|
||||
// involvement at all) are DELIBERATELY not the same number,
|
||||
// not an unreconciled oversight: neither carries a retail
|
||||
// decomp citation (both are acdream-synthesized small
|
||||
// insets), and they answer different questions — this one
|
||||
// is "gap after a REAL adjacent face element" (a geometry-
|
||||
// derived offset), the other is "default left inset when
|
||||
// there is no reference geometry at all" (a context-free
|
||||
// fallback). Moving either number to match the other would
|
||||
// be an unfounded 1px guess on whichever button currently
|
||||
// works, not a fix — see DatWidgetFactoryTests' own
|
||||
// `face.X(0) + face.Width(32) + 4` pin for this exact site.
|
||||
button.LabelOffsetX = face.X + face.Width + 4f;
|
||||
}
|
||||
}
|
||||
|
|
@ -932,7 +948,10 @@ public static class DatWidgetFactory
|
|||
// Type-12 child, and the built row's LabelAlign came out Center.
|
||||
// labelInfo.X is only a valid inner-offset when a distinct child
|
||||
// was actually lifted; for the direct (labelInfo == info) case,
|
||||
// leave UiButton's own default 3px LabelOffsetX in place.
|
||||
// leave UiButton's own default 3px LabelOffsetX in place — see
|
||||
// the face-relative +4f branch above (F10) for why this 3px
|
||||
// default and that 4px gap are deliberately different numbers,
|
||||
// not an unreconciled asymmetry.
|
||||
button.LabelAlign = UiButton.LabelAlignment.Left;
|
||||
if (!ReferenceEquals(labelInfo, info))
|
||||
button.LabelOffsetX = labelInfo.X;
|
||||
|
|
|
|||
|
|
@ -189,7 +189,21 @@ public static class LayoutImporter
|
|||
{
|
||||
if (child.StateMedia.Count == 0) continue;
|
||||
var cw = BuildWidget(child, resolve, datFont, fontResolve, stringResolve, byId);
|
||||
if (cw is not null) w.AddChild(cw);
|
||||
if (cw is null) continue;
|
||||
// F5/F6 (Campaign CC gate round 1 closeout): a NARROW honor
|
||||
// of AuthoredInvisible, scoped to children reached through
|
||||
// THIS carve-out only — e.g. the chat new-text indicator
|
||||
// (0x1000048C, live-DAT-confirmed Invisible=true on every
|
||||
// layout it appears in) would otherwise render as a phantom
|
||||
// element retail never shows, now that this carve-out
|
||||
// builds it as a real widget instead of silently dropping
|
||||
// it. This is NOT the general client-wide honor (#408,
|
||||
// 1,083 elements) — every OTHER AuthoredInvisible consumer
|
||||
// stays data-only, acted on nowhere but chargen's own
|
||||
// HideAuthoredInvisibleElements walk (register AP-230).
|
||||
if (cw.AuthoredInvisible)
|
||||
cw.Visible = false;
|
||||
w.AddChild(cw);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue