fix(chargen): Campaign CC gate round 1 Batch A — GF-15 input, GF-5 skills rows, GF-13 GM toggles
GF-15 (the gate blocker): the Summary name field and Finish button were
NOT structurally broken — live repro over the project's own local ACE
test server showed clicks correctly focus the field and land characters.
The real bug only surfaces after the first dialog opens: pressing Finish
empty successfully creates the NoName RetailMessageDialogView (visible,
correct 400x95 geometry) but it renders nothing and silently absorbs
every click across the whole canvas. Root cause: CharacterCreationUiController.Tick
and CharacterManagementUiController.Tick both call UiRoot.BringToFront(Root)
unconditionally every frame (needed so chargen stays above the occluded
management screen, AP-229); a dialog root is a direct sibling under the
same UiRoot, and RetailWindowManager.BringToFront is "highest ZOrder among
siblings + 1" — whichever BringToFront runs last in a frame wins.
RetailDialogFactory.Tick never re-asserted its own dialogs' z-order, so
the next frame's screen Tick buried the dialog behind the screen's opaque
backdrop while it stayed the registered Modal with exclusive input
priority. Fixed by having RetailDialogFactory.Tick re-raise every open
dialog (in open-order) each tick, matching retail's always-on-top dialog
behavior. Live-verified the complete user sequence end to end: click
field, type, press Finish empty, dialog now visibly renders, OK dismisses
cleanly, field still typable afterward. The "[ Name" prefill question is
closed as a non-bug: neither CharGenState::RandomizeCharacter nor
gmCGSummaryPage::InitializePage write text into the field in the decomp;
retail's field is genuinely empty on open, matching acdream already.
GF-5: CharacterCreationSkillsPage.RebuildRows resolved the wrong listbox
template (Templates[0], retail's own 3-child bucket-header row) and
required the root to be a UiButton (it's a plain container). Byte-traced
gmCGSkillsPage::DoSkillRecords + tagSkillRecord's copy-ctor field order
to map every child id in the real row (Templates[1]): name, level/cost
text, and the two real per-row up/down arrow buttons. Wired the arrows to
retail's own plain-click dispatch, retiring (narrowing) AP-213's
click-to-advance/double-click-retreat single-button substitution.
GF-13: dat property 0x3B (Invisible) was never read by the importer.
Elements 0x10000403/0x10000494 ("Non-Admin"/"Non-Envoy") author it true.
A blast-radius sweep found 1,083 elements client-wide author the same
flag, so this fix stays chargen-scoped only (ElementInfo.Invisible /
UiElement.AuthoredInvisible are pure data additions; only
CharacterCreationUiController acts on them, by the authored flag, not a
hardcoded id list). General importer-wide honor filed as ISSUES.md #408;
register row AP-230 records the split.
Gates: solution build green; App 5266/3 skips/0 failed; Runtime 1735/0;
full-solution run 0 failures anywhere. Register: AP-230 filed, AP-213
narrowed. ISSUES: #408 filed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
6699e0f88c
commit
1d9de5e095
11 changed files with 823 additions and 78 deletions
|
|
@ -308,6 +308,41 @@ internal sealed class CharacterCreationUiController : IDisposable
|
|||
_appearanceTab.OnClick = () => ApplyProgressState(Page.Appearance);
|
||||
_townTab.OnClick = () => ApplyProgressState(Page.Town);
|
||||
_summaryTab.OnClick = () => ApplyProgressState(Page.Summary);
|
||||
|
||||
// GF-13 (Campaign CC gate round 1, Batch A): honor the authored
|
||||
// Invisible flag (dat property 0x3B) chargen-scoped only — see
|
||||
// HideAuthoredInvisibleElements's own doc comment.
|
||||
HideAuthoredInvisibleElements(Root);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GF-13 (Campaign CC gate round 1, Batch A). The user's live gate
|
||||
/// reported an acdream-only "-Non-admin or Non-envoy" text leak below the
|
||||
/// Summary name field. Root cause: elements <c>0x10000403</c> ("Non-
|
||||
/// Admin") and <c>0x10000494</c> ("Non-Envoy") author dat property
|
||||
/// <c>0x3B</c> (Invisible) = <see langword="true"/> — retail's
|
||||
/// <c>UIElement::OnSetAttribute @0x00462d80</c> case 8
|
||||
/// (<c>GetPropertyName()-0x33 == 8</c>, property id <c>0x3B</c>) hides any
|
||||
/// element authoring it via <c>SetVisible(value == 0)</c>. acdream's
|
||||
/// shared <see cref="LayoutImporter"/> never read this property at all
|
||||
/// (it now does, into <see cref="ElementInfo.Invisible"/> /
|
||||
/// <see cref="UiElement.AuthoredInvisible"/>, a pure data addition), so
|
||||
/// every one of the 1,083 elements client-wide that author it rendered
|
||||
/// regardless. A blanket importer-wide honor is its own separately-gated
|
||||
/// visual sweep (docs/ISSUES.md #408) — this method is the NARROW,
|
||||
/// chargen-scoped fix: walk this screen's own mounted subtree once at
|
||||
/// construction and hide anything the dat itself marked hidden, by the
|
||||
/// AUTHORED FLAG rather than a hardcoded id list, so any other
|
||||
/// authored-invisible element under this root (not just the two the user
|
||||
/// happened to see) is honored the same way. Register AP-230 records the
|
||||
/// scoped-vs-general split.
|
||||
/// </summary>
|
||||
private static void HideAuthoredInvisibleElements(UiElement element)
|
||||
{
|
||||
if (element.AuthoredInvisible)
|
||||
element.Visible = false;
|
||||
foreach (UiElement child in element.Children)
|
||||
HideAuthoredInvisibleElements(child);
|
||||
}
|
||||
|
||||
internal UiElement Root => _layout.Root;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue