fix(chargen): Campaign CC gate round 1 Batch F — Skills page buckets, selection, info box, cost text, arrow states
R2-4/review F1-F2 (gmCGSkillsPage): row click (and arrow click, matching retail's own post-Increase/DecreaseSkillLevel re-select) now selects a skill, highlights its row name, and writes the info panes' title (name + score) and a level-gated bonus line; the description/formula halves stay unported (SkillBase._description/_formula unreachable from this page's current data surface, documented on RefreshInfoBox). The listbox's own authored scrollbar link is wired to its Scroll model (live-DAT-confirmed at 0x100003F8, matching the "+1 from the listbox" hypothesis). Cost text now matches SetSkillText @0x00480600 exactly: Untrained's down-cost and Specialized's up-cost are literal "0", unconditional, where the port previously rendered blank; the 999-blank gate applies to the up-cost only, never to a down-cost. Arrow Ghosted/Enabled state (0x1000001a/ 0x1000001b) is now gated per branch, including bUntrainable/ bUnspecializable re-derived as "this row's own effective cost is nonzero" — no new data needed since the page already resolves that cost. R2-4b (the four-bucket sorted model) is NOT implemented — its Useable- vs-Unuseable-Untrained split reads SkillBase.MinLevel, confirmed present in the installed dat (SkillTable_MinLevelDistribution_NeverExceedsTrained) but not threaded through ChargenOptions/ChargenHeritageOptions/ CharacterCreationRuntimeBindings. AP-213 row records the exact channel a future fix needs. Also live-DAT-pinned: Templates[0]'s header-caption child (0x100002f6) resolves as a UiButton, not UiText, in the real dat — the same UIElement_Button-is-DynamicCast(0xc)-compatible-with-Text quirk already ported for GF-4b's slider labels. App suite (live-DAT env) 5321/3 -> 5328/3 (+7, zero regressions). Runtime 1735/0 unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
2ad805469d
commit
8c30aa18ee
5 changed files with 615 additions and 33 deletions
|
|
@ -1045,6 +1045,66 @@ public sealed class CharacterCreationLiveDatTests
|
|||
Assert.Equal(0x100002F4u, list.Templates[0].TemplateElementId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// R2-4c (Campaign CC gate round 1 Batch F). Live-DAT probe: pins the
|
||||
/// Skills listbox's own authored scrollbar link (dat property
|
||||
/// <c>0x72</c>, <see cref="UiTemplateListBox.ScrollbarElementId"/>) —
|
||||
/// CONFIRMED as <c>0x100003F8</c>, exactly the "+1 from the listbox"
|
||||
/// hypothesis this batch's own investigation raised — and confirms a
|
||||
/// real <see cref="UiScrollbar"/> resolves at that id under the Skills
|
||||
/// page root, the exact fact
|
||||
/// <see cref="CharacterCreationSkillsPage"/>'s constructor now wires
|
||||
/// (<c>scrollbar.Model = list.Scroll</c>).
|
||||
///
|
||||
/// <para>
|
||||
/// Also pins <c>Templates[0]</c>'s (<c>0x100002F4</c>) own header-
|
||||
/// caption child id (<c>0x100002f6</c>, <c>DoSkillRecords @0x00481840</c>'s
|
||||
/// own <c>GetChildRecursive</c> call) as prep evidence for whoever ports
|
||||
/// the four-bucket sorted model (R2-4b, still open — see the AP-213
|
||||
/// row): live-DAT-measured as a <see cref="UiButton"/>, NOT a
|
||||
/// <see cref="UiText"/> — the SAME <c>UIElement_Button</c>-is-
|
||||
/// <c>DynamicCast(0xc)</c>-compatible-with-<c>UIElement_Text</c> quirk
|
||||
/// this campaign already ported for the six attribute-slider labels
|
||||
/// (GF-4b) — retail's own <c>DynamicCast(0xc)</c> cast at
|
||||
/// <c>0x00481855</c> would return null on a REAL Button object
|
||||
/// otherwise, and the very next line unconditionally calls
|
||||
/// <c>UIElement_Text::SetStringInfo</c> on it. A future port reads the
|
||||
/// header caption through <c>UiButton.Label</c>, the same seam GF-4b
|
||||
/// already established.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[InstalledDatFact]
|
||||
public void SkillsPage_Listbox_HasAScrollbarLink_AndHeaderTemplateHasACaptionChild()
|
||||
{
|
||||
using var dats = new DatCollection(DatDirectory, DatAccessType.Read);
|
||||
uint layoutId = RetailDataIdResolver.Resolve(
|
||||
dats, CharacterCreationUiController.RootEnum, 5u);
|
||||
ImportedLayout screen = BuildSelected(
|
||||
dats, layoutId, CharacterCreationUiController.RootElementId);
|
||||
|
||||
UiElement skillsRoot = Assert.IsAssignableFrom<UiElement>(
|
||||
screen.FindElement(CharacterCreationUiController.SkillsPageElementId));
|
||||
UiTemplateListBox list = Assert.IsType<UiTemplateListBox>(
|
||||
UiElement.FindDescendant(skillsRoot, 0x100003F7u));
|
||||
|
||||
Console.WriteLine(
|
||||
$"[CC-Batch-F-DAT] Skills listbox ScrollbarElementId=0x{list.ScrollbarElementId:X8}");
|
||||
Assert.Equal(0x100003F8u, list.ScrollbarElementId);
|
||||
Assert.IsType<UiScrollbar>(
|
||||
UiElement.FindDescendant(skillsRoot, list.ScrollbarElementId));
|
||||
|
||||
UiTemplateListEntry headerTemplate = list.Templates[0];
|
||||
Assert.Equal(0x100002F4u, headerTemplate.TemplateElementId);
|
||||
UiElement? headerRow = LayoutImporter.Import(
|
||||
dats,
|
||||
headerTemplate.TemplateLayoutId,
|
||||
headerTemplate.TemplateElementId,
|
||||
_ => (0u, 0, 0),
|
||||
null)?.Root;
|
||||
UiElement realHeaderRow = Assert.IsAssignableFrom<UiElement>(headerRow);
|
||||
Assert.IsType<UiButton>(UiElement.FindDescendant(realHeaderRow, 0x100002F6u));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GF-15 (Campaign CC gate round 1, Batch A). Live-DAT-probe-confirmed
|
||||
/// during the investigation: the Message dialog catalog's popup
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue