fix(chargen): Campaign CC gate round 1 Batch C — Summary how-to + scrollbar linkage
Commit 3/3: Summary how-to text + Commit 2's owed scrollbar linkage + bookkeeping sweep. - Ports gmCGSummaryPage::SetHowToText @0x0047ae20 into the Summary page's how-to box (0x10000404, HowToTextId was declared and unused since CC5). Retail concatenates ID_CharGen_SummaryHowTo + a heritage/ gender-specific name-suggestion list (heritages 1-4 — Aluvian/ Gharundim/Sho/Viamontian — only; heritages 5-13's cases in the same switch decompile to a vtable-slot artifact, the same decompiler- mangled-symbol class the Heritage page's own BonusSkillsKeyByHeritage table already documents, so no name-suggestion string exists for them and none is invented) + ID_CharGen_SummaryHowToEnd, directly concatenated (no separator literal) into ONE plain SetText call — no per-run font/color argument, unlike Heritage's ...WithFont calls, so this routes through DatRichText as a single DefaultColor segment. - Wires the description boxes' linked scrollbar to actual text scrolling — Commit 2 made the scrollbar child (0x100002e7) BUILD as a real UiScrollbar; this binds scrollbar.Model = text.Scroll, the exact pattern ChatWindowController already uses for the chat transcript. Live-DAT-measured: only Heritage's description (0x100003c4) and Summary's how-to box (0x10000404) actually author this child — Profession/Town's shorter description boxes do not (a genuine retail authoring fact, not something to "fix" further). Register: AP-215/AP-216/AP-217 rewritten (Batch C's Commit 1 already retired AP-218/AD-103) — no further changes needed this commit; ISSUES #366 (chat's new-unseen-text indicator, 0x1000048C under the chat transcript 0x10000011) NARROWED — its own pre-filed "fix shape" recommendation (a UiText child carve-out mirroring UiMeter's) is EXACTLY what Commit 2 shipped, confirmed by that commit's own client-wide sweep; #366 stays open for the still-missing behavioral half (no controller drives the indicator's visibility/click). Findings doc updated: GF-2/GF-3/GF-4/GF-6/GF-11a/GF-12/GF-14's text half all marked FIXED with their own root-cause notes; the two remaining "suspected shared roots" (frames/labels, rich text) marked CONFIRMED + CLOSED. Full App suite (Debug and Release, live-DAT): 5307 passed / 0 failed / 3 skipped (up from 5304 after Commit 2). Runtime suite: 1735/0, unaffected. Campaign CC gate round 1 Batch C is CODE-COMPLETE across all three commits — GF-2, GF-3, GF-4, GF-6, GF-11a, GF-12, and GF-14's text half are fixed; AP-216/AP-217 partially closed (register-honest about what shipped vs what needs a palette-to-RGB pipeline this batch didn't add). Pending the user's visual gate, with chat + the main game UI flagged for extra attention (Commit 2's client-wide blast radius). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
5190e16915
commit
2349f8b4df
6 changed files with 391 additions and 45 deletions
|
|
@ -135,6 +135,19 @@ internal sealed class CharacterCreationHeritagePage : IDisposable
|
|||
|
||||
_description = UiElement.FindDescendant(pageRoot, 0x100003C4u) as UiText;
|
||||
_backdrop = UiElement.FindDescendant(pageRoot, 0x100003BEu);
|
||||
|
||||
// Commit 2/3 follow-up (Campaign CC gate round 1 Batch C): the
|
||||
// description box's own linked scrollbar — live-DAT-measured
|
||||
// present here (unlike Profession/Town's shorter description
|
||||
// boxes, which author no scrollbar child at all) at the SAME
|
||||
// relative id CharacterCreationSummaryPage's how-to box carries.
|
||||
// ChatWindowController's own scrollbar.Model = transcript.Scroll
|
||||
// pattern, scoped to this box's own descendant.
|
||||
if (_description is not null
|
||||
&& UiElement.FindDescendant(_description, 0x100002E7u) is UiScrollbar descriptionScroll)
|
||||
{
|
||||
descriptionScroll.Model = _description.Scroll;
|
||||
}
|
||||
}
|
||||
|
||||
internal void Refresh(
|
||||
|
|
|
|||
|
|
@ -76,11 +76,48 @@ internal sealed class CharacterCreationSummaryPage : IDisposable
|
|||
/// </summary>
|
||||
private const int MaxNameLength = 32;
|
||||
|
||||
/// <summary>
|
||||
/// Commit 3 (Campaign CC gate round 1 Batch C): the how-to box's own
|
||||
/// linked scrollbar, relative id — live-DAT-measured as the SAME
|
||||
/// template child id the Heritage description box also carries
|
||||
/// (<c>0x100002e7</c>), distinct from <see cref="ScrollId"/> (the
|
||||
/// listbox's own scrollbar). Profession/Town's description boxes do
|
||||
/// NOT author this child at all (shorter authored text, live-DAT-
|
||||
/// confirmed) — only Heritage and Summary's how-to box do.
|
||||
/// </summary>
|
||||
private const uint HowToScrollRelativeId = 0x100002E7u;
|
||||
|
||||
/// <summary>
|
||||
/// Heritage id -> (male name-list key, female name-list key) per
|
||||
/// <c>gmCGSummaryPage::SetHowToText @0x0047ae20</c>'s switch
|
||||
/// (@0x0047aeb2-0x0047afda). ONLY heritages 1-4 (Aluvian/Gharundim/
|
||||
/// Sho/Viamontian) resolve to real string literals
|
||||
/// (<c>"ID_CharGen_<Abbrev>{Male,Female}Names"</c>, confirmed
|
||||
/// present in the compiled string-constant table); every other
|
||||
/// heritage's case in that same switch (5/0xa Shadowbound+Penumbraen
|
||||
/// share one body, 6 Gearknight, 7 Tumerok, 8 Lugian, 9 Empyrean, 0xb
|
||||
/// Undead, 0xc/0xd Olthoi/OlthoiAcid) decompiles to a vtable-slot
|
||||
/// artifact instead of a string constant — the same decompiler-
|
||||
/// mangled-symbol class the Heritage page's own
|
||||
/// <c>BonusSkillsKeyByHeritage</c> table already documents — meaning
|
||||
/// no real name-suggestion string exists for those heritages; this
|
||||
/// port does not invent one.
|
||||
/// </summary>
|
||||
private static readonly IReadOnlyDictionary<uint, (string Male, string Female)> NameSuggestionKeysByHeritage =
|
||||
new Dictionary<uint, (string, string)>
|
||||
{
|
||||
[(uint)ChargenHeritageGroup.Aluvian] = ("ID_CharGen_AluMaleNames", "ID_CharGen_AluFemaleNames"),
|
||||
[(uint)ChargenHeritageGroup.Gharundim] = ("ID_CharGen_GharuMaleNames", "ID_CharGen_GharuFemaleNames"),
|
||||
[(uint)ChargenHeritageGroup.Sho] = ("ID_CharGen_ShoMaleNames", "ID_CharGen_ShoFemaleNames"),
|
||||
[(uint)ChargenHeritageGroup.Viamontian] = ("ID_CharGen_ViaMaleNames", "ID_CharGen_ViaFemaleNames"),
|
||||
};
|
||||
|
||||
private readonly CharacterCreationRuntimeBindings _bindings;
|
||||
private readonly RetailDialogFactory _dialogs;
|
||||
private readonly string _nameTooLongMessage;
|
||||
private readonly UiTemplateListBox? _list;
|
||||
private readonly UiField? _nameField;
|
||||
private readonly UiText? _howToText;
|
||||
private string _lastCommittedName = string.Empty;
|
||||
private uint _nameTooLongDialogContext;
|
||||
private bool _disposed;
|
||||
|
|
@ -145,6 +182,19 @@ internal sealed class CharacterCreationSummaryPage : IDisposable
|
|||
}
|
||||
|
||||
Viewport = UiElement.FindDescendant(pageRoot, ViewportId) as UiViewport;
|
||||
|
||||
// Commit 2/3 follow-up: the how-to box's linked scrollbar (Commit
|
||||
// 2 made it BUILD as a real UiScrollbar; this wires it to actual
|
||||
// scrolling) — ChatWindowController's own scrollbar.Model =
|
||||
// transcript.Scroll pattern, scoped to THIS box's own descendant
|
||||
// (the relative id recurs on Heritage's description box too, so a
|
||||
// flat screen-wide lookup would be ambiguous).
|
||||
_howToText = UiElement.FindDescendant(pageRoot, HowToTextId) as UiText;
|
||||
if (_howToText is not null
|
||||
&& UiElement.FindDescendant(_howToText, HowToScrollRelativeId) is UiScrollbar howToScroll)
|
||||
{
|
||||
howToScroll.Model = _howToText.Scroll;
|
||||
}
|
||||
}
|
||||
|
||||
internal void Refresh(
|
||||
|
|
@ -176,6 +226,51 @@ internal sealed class CharacterCreationSummaryPage : IDisposable
|
|||
|
||||
RebuildListbox(view, snapshot);
|
||||
RebuildPreview(view, snapshot);
|
||||
RefreshHowToText(snapshot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Commit 3 (Campaign CC gate round 1 Batch C): ports
|
||||
/// <c>gmCGSummaryPage::SetHowToText @0x0047ae20</c>. Retail
|
||||
/// concatenates <c>ID_CharGen_SummaryHowTo</c> +
|
||||
/// (heritage/gender-specific name-suggestion list, heritages 1-4
|
||||
/// only) + <c>ID_CharGen_SummaryHowToEnd</c> directly
|
||||
/// (<c>append_n_chars</c>, no separator literal) into ONE plain
|
||||
/// <c>UIElement_Text::SetText</c> — no per-run font/color argument,
|
||||
/// unlike Heritage's <c>...WithFont</c> calls, so this is a single
|
||||
/// <see cref="UiText.DefaultColor"/> segment.
|
||||
/// </summary>
|
||||
private void RefreshHowToText(RuntimeCharacterCreationSnapshot snapshot)
|
||||
{
|
||||
if (_howToText is null)
|
||||
return;
|
||||
|
||||
Func<string, string?>? resolveText = _bindings.ResolveText;
|
||||
if (resolveText is null)
|
||||
return;
|
||||
|
||||
var builder = new System.Text.StringBuilder();
|
||||
if (resolveText("ID_CharGen_SummaryHowTo") is { } howTo)
|
||||
builder.Append(howTo);
|
||||
if (NameSuggestionKeysByHeritage.TryGetValue(snapshot.HeritageId, out (string Male, string Female) keys))
|
||||
{
|
||||
// gmCGSummaryPage::SetHowToText @0x0047af3f et al.: the raw
|
||||
// "!= 2" comparison, no gender-unset special case — an unset
|
||||
// gender (0) takes the male-key branch, matching retail's own
|
||||
// literal comparison.
|
||||
string key = snapshot.GenderKey == 2u ? keys.Female : keys.Male;
|
||||
if (resolveText(key) is { } nameTokens)
|
||||
builder.Append(nameTokens);
|
||||
}
|
||||
if (resolveText("ID_CharGen_SummaryHowToEnd") is { } howToEnd)
|
||||
builder.Append(howToEnd);
|
||||
|
||||
if (builder.Length == 0)
|
||||
return;
|
||||
|
||||
string composed = builder.ToString();
|
||||
var segments = new[] { new DatRichText.Segment(composed, _howToText.DefaultColor) };
|
||||
_howToText.LinesProvider = () => DatRichText.Compose(_howToText, segments);
|
||||
}
|
||||
|
||||
// ── Name field (ListenToElementMessage @ 0x0047bf40) ────────────────
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue