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
|
|
@ -542,6 +542,35 @@ public sealed class CharacterCreationLiveDatTests
|
|||
"ID_CharGen_ShoushiText",
|
||||
"ID_CharGen_YaraqText",
|
||||
"ID_CharGen_SanamarText",
|
||||
// GF-3: Profession template description keys.
|
||||
"ID_CharGen_CustomText",
|
||||
"ID_CharGen_BowText",
|
||||
"ID_CharGen_SwashText",
|
||||
"ID_CharGen_LifeText",
|
||||
"ID_CharGen_WarText",
|
||||
"ID_CharGen_WayText",
|
||||
"ID_CharGen_SoldierText",
|
||||
// GF-6/AP-218: Appearance spin caption keys.
|
||||
"ID_CharGen_HairStyle",
|
||||
"ID_CharGen_Eyes",
|
||||
"ID_CharGen_Skin",
|
||||
"ID_CharGen_GearText_HairButton",
|
||||
"ID_CharGen_GearText_EyesButton",
|
||||
"ID_CharGen_GearText_SkinButton",
|
||||
"ID_CharGen_OlthoiText_HairButton",
|
||||
"ID_CharGen_OlthoiText_EyesButton",
|
||||
"ID_CharGen_OlthoiText_SkinButton",
|
||||
// Commit 3: Summary how-to text keys.
|
||||
"ID_CharGen_SummaryHowTo",
|
||||
"ID_CharGen_SummaryHowToEnd",
|
||||
"ID_CharGen_AluMaleNames",
|
||||
"ID_CharGen_AluFemaleNames",
|
||||
"ID_CharGen_GharuMaleNames",
|
||||
"ID_CharGen_GharuFemaleNames",
|
||||
"ID_CharGen_ShoMaleNames",
|
||||
"ID_CharGen_ShoFemaleNames",
|
||||
"ID_CharGen_ViaMaleNames",
|
||||
"ID_CharGen_ViaFemaleNames",
|
||||
];
|
||||
foreach (string key in keys)
|
||||
{
|
||||
|
|
@ -1239,6 +1268,53 @@ public sealed class CharacterCreationLiveDatTests
|
|||
Assert.IsType<UiScrollbar>(UiElement.FindDescendant(summaryHowTo, 0x100002E7u));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Commit 3: the Heritage description box (which ALSO carries the
|
||||
/// linked scrollbar — live-DAT-measured, unlike Profession/Town's
|
||||
/// shorter description boxes) resolves it as a real widget too, and
|
||||
/// the mount wires <see cref="UiScrollbar.Model"/> to the box's own
|
||||
/// <see cref="UiText.Scroll"/>.
|
||||
/// </summary>
|
||||
[InstalledDatFact]
|
||||
public void HeritageDescription_ScrollbarBuildsAndLinksToTextScroll()
|
||||
{
|
||||
using var dats = new DatCollection(DatDirectory, DatAccessType.Read);
|
||||
uint layoutId = RetailDataIdResolver.Resolve(
|
||||
dats, CharacterCreationUiController.RootEnum, 5u);
|
||||
ImportedLayout screen = BuildSelected(
|
||||
dats, layoutId, CharacterCreationUiController.RootElementId);
|
||||
|
||||
UiElement heritageRoot = Assert.IsAssignableFrom<UiElement>(
|
||||
screen.FindElement(CharacterCreationUiController.HeritagePageElementId));
|
||||
UiText description = Assert.IsType<UiText>(
|
||||
UiElement.FindDescendant(heritageRoot, 0x100003C4u));
|
||||
UiScrollbar scroll = Assert.IsType<UiScrollbar>(
|
||||
UiElement.FindDescendant(description, 0x100002E7u));
|
||||
|
||||
var host = new UiRoot();
|
||||
var dialogs = MakeDialogFactory(dats, host);
|
||||
var bindings = new CharacterCreationRuntimeBindings(
|
||||
() => null,
|
||||
_ => default, _ => default, _ => default, (_, _) => default, (_, _) => default,
|
||||
_ => default, _ => default, _ => default, _ => default, _ => default, () => { });
|
||||
UiElement? ResolveTemplate(uint templateLayoutId, uint templateElementId) =>
|
||||
LayoutImporter.Import(
|
||||
dats, templateLayoutId, templateElementId, _ => (0u, 0, 0), null)?.Root;
|
||||
|
||||
CharacterCreationUiController? controller =
|
||||
CharacterCreationUiController.CreateDetached(
|
||||
host, screen, ResolveTemplate, dialogs, bindings,
|
||||
new CharacterCreationUiController.DialogStrings(
|
||||
"Are you sure?", "No name", "Unspent credits", "Randomize?", "Name too long"));
|
||||
Assert.NotNull(controller);
|
||||
controller!.AttachAndTick();
|
||||
|
||||
Assert.Same(description.Scroll, scroll.Model);
|
||||
|
||||
controller.Dispose();
|
||||
dialogs.Dispose();
|
||||
}
|
||||
|
||||
private static void AssertButton(ImportedLayout layout, uint elementId) =>
|
||||
Assert.IsType<UiButton>(layout.FindElement(elementId));
|
||||
|
||||
|
|
|
|||
|
|
@ -1663,6 +1663,66 @@ public sealed class CharacterCreationUiControllerTests
|
|||
Assert.Equal(0x1000002Cu, professionBackdrop.ActiveRetailStateId);
|
||||
}
|
||||
|
||||
/// <summary>Commit 3: the Summary how-to text concatenates HowTo +
|
||||
/// the heritage/gender name-suggestion list (heritages 1-4 only) +
|
||||
/// HowToEnd, in that order, no separator inserted by code.</summary>
|
||||
[Fact]
|
||||
public void SummaryHowToText_ComposesHowToPlusNameSuggestionsPlusHowToEnd_ForNamedHeritages()
|
||||
{
|
||||
using var environment = new EnvironmentHarness();
|
||||
environment.Runtime.ResolvedStrings["ID_CharGen_SummaryHowTo"] = "HOWTO.";
|
||||
environment.Runtime.ResolvedStrings["ID_CharGen_SummaryHowToEnd"] = "HOWTOEND.";
|
||||
environment.Runtime.ResolvedStrings["ID_CharGen_AluMaleNames"] = "Alucard, Aldric";
|
||||
environment.Runtime.ResolvedStrings["ID_CharGen_AluFemaleNames"] = "Alura, Aldyth";
|
||||
environment.Controller.Open();
|
||||
|
||||
environment.Runtime.SelectHeritageDirect(AluvianId);
|
||||
environment.Runtime.SelectGenderDirect(1u); // male
|
||||
environment.TabButton(CharacterCreationUiController.SummaryTabElementId).OnClick!();
|
||||
BumpRevisionAndTick(environment);
|
||||
|
||||
UiText howTo = Assert.IsType<UiText>(
|
||||
environment.Screen.FindElement(CharacterCreationSummaryPage.HowToTextId));
|
||||
string composed = JoinedText(howTo);
|
||||
Assert.Contains("HOWTO.", composed);
|
||||
Assert.Contains("Alucard, Aldric", composed);
|
||||
Assert.DoesNotContain("Alura, Aldyth", composed);
|
||||
Assert.Contains("HOWTOEND.", composed);
|
||||
// No code-inserted separator: HowTo's own tail must be immediately
|
||||
// followed by the name-list segment's own head, character for
|
||||
// character (only whichever whitespace the AUTHORED strings
|
||||
// themselves carry — none, in this test's fixture strings).
|
||||
Assert.Contains("HOWTO.Alucard, Aldric", composed.Replace("\n", string.Empty));
|
||||
|
||||
environment.Runtime.SelectGenderDirect(2u); // female
|
||||
BumpRevisionAndTick(environment);
|
||||
string femaleComposed = JoinedText(howTo);
|
||||
Assert.Contains("Alura, Aldyth", femaleComposed);
|
||||
Assert.DoesNotContain("Alucard, Aldric", femaleComposed);
|
||||
}
|
||||
|
||||
/// <summary>Heritages without a real retail name-suggestion string
|
||||
/// (5-13, the decompiler-artifact cases) compose HowTo directly
|
||||
/// against HowToEnd — no invented text, no crash.</summary>
|
||||
[Fact]
|
||||
public void SummaryHowToText_SkipsNameSuggestions_ForHeritagesWithNoRealString()
|
||||
{
|
||||
using var environment = new EnvironmentHarness();
|
||||
environment.Runtime.ResolvedStrings["ID_CharGen_SummaryHowTo"] = "HOWTO.";
|
||||
environment.Runtime.ResolvedStrings["ID_CharGen_SummaryHowToEnd"] = "HOWTOEND.";
|
||||
environment.Controller.Open();
|
||||
|
||||
environment.Runtime.SelectHeritageDirect((uint)ChargenHeritageGroup.Undead);
|
||||
environment.TabButton(CharacterCreationUiController.SummaryTabElementId).OnClick!();
|
||||
BumpRevisionAndTick(environment);
|
||||
|
||||
UiText howTo = Assert.IsType<UiText>(
|
||||
environment.Screen.FindElement(CharacterCreationSummaryPage.HowToTextId));
|
||||
string composed = JoinedText(howTo);
|
||||
Assert.Contains("HOWTO.", composed);
|
||||
Assert.Contains("HOWTOEND.", composed);
|
||||
}
|
||||
|
||||
private static void BumpRevisionAndTick(EnvironmentHarness environment)
|
||||
{
|
||||
RuntimeCharacterCreationSnapshot snapshot = environment.Runtime.View.Snapshot;
|
||||
|
|
@ -1897,6 +1957,7 @@ public sealed class CharacterCreationUiControllerTests
|
|||
public Dictionary<string, string> ResolvedStrings { get; } = [];
|
||||
|
||||
public void SelectHeritageDirect(uint heritageId) => SelectHeritage(heritageId);
|
||||
public void SelectGenderDirect(uint genderKey) => SelectGender(genderKey);
|
||||
|
||||
public ChargenSkillAdvancementClass GetSkillLevel(uint skillId) =>
|
||||
View.GetSkillLevel(skillId);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue