fix(chargen): Campaign CC gate round 1 re-test 3 — R4-1..R4-4
Four visual residuals from the lead's own live-client captures of 1.0.2-cc.m, all root-caused via decomp + live-DAT evidence: - R4-1: Skills credits value overlapped mid-caption again. Root cause was a missing UiLayoutPolicy raw-edge reflow on UiButton's value-child rect (the child is base-inherited across four sibling buttons of differing widths, so its baked-in OriginalParentWidth diverges from the actual 231px-wide Skills credits button) plus an HJustify.Right value child mapped to Center instead of a real far-edge Right. - R4-2: the single-sprite scrollbar thumb tiled (GL_REPEAT) instead of drawing once — DrawTiled was reused for a small fixed marker graphic whose native size is far smaller than the track-proportional thumb rect. New DrawThumbMarker draws exactly one native-size instance. - R4-3: the skills info-box formula line clipped past the surrounding gold frame's own authored bottom edge (the pane's own raw box is 20px taller than the frame that visually contains it) — clamp the pane's Height to the frame's bottom (register AD-105, since retail's ShowSkillsText has no code relationship to the frame to cite). - R4-4: the Appearance help text started mid-sentence — the box was never touched by its page controller, so it kept UiText's chat-style PreserveEndOnLayout=true default; the scroll model's wasAtEnd check is vacuously true on its first-ever overflow transition, pinning the first render to the bottom. Set PreserveEndOnLayout=false (a static top-oriented report, not a transcript) and wired the box's own nested authored scrollbar, never wired before. App suite live-DAT env 5372/3 -> 5379/3 (+7, zero regressions). Runtime 1735/0 unchanged. Full solution 14585/4 skips/1 failure (the documented Core.Net NakEmission full-solution-only flake, confirmed standalone-pass). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
28704db4bf
commit
e6acb800cc
11 changed files with 678 additions and 12 deletions
|
|
@ -1009,14 +1009,29 @@ public static class DatWidgetFactory
|
|||
child => child.Type == 12u && child.StateMedia.Count == 0);
|
||||
if (valueChild is not null)
|
||||
{
|
||||
button.ValueBox = (valueChild.X, valueChild.Y, valueChild.Width, valueChild.Height);
|
||||
// R4-1 (Campaign CC gate round 1 re-test 3): reflow the value
|
||||
// child's authored rect through retail's own raw-edge policy
|
||||
// (UIElement::UpdateForParentSizeChange @0x00462640, ported
|
||||
// as UiLayoutPolicy) before it becomes ValueBox — see
|
||||
// ReflowValueChildRect's own doc for why this is needed and
|
||||
// decomp-cited.
|
||||
button.ValueBox = ReflowValueChildRect(valueChild, info);
|
||||
button.ValueFont = valueChild.FontDid != 0u && fontResolve is not null
|
||||
? fontResolve(valueChild.FontDid) ?? elementFont
|
||||
: elementFont;
|
||||
button.ValueColor = valueChild.FontColor ?? System.Numerics.Vector4.One;
|
||||
button.ValueAlign = valueChild.HJustify == HJustify.Left
|
||||
? UiButton.LabelAlignment.Left
|
||||
: UiButton.LabelAlignment.Center;
|
||||
button.ValueAlign = valueChild.HJustify switch
|
||||
{
|
||||
HJustify.Left => UiButton.LabelAlignment.Left,
|
||||
// R4-1: HJustify.Right (raw dat 3/5) previously fell into
|
||||
// this ternary's Center branch — CalcJustification's own
|
||||
// ecx_5==3||5 case is a DISTINCT far-edge formula (see
|
||||
// UiButton.LabelAlignment.Right's own doc), and every
|
||||
// value child in this family (0x100002f1/0x100002f3)
|
||||
// authors HJustify Right, live-DAT-confirmed.
|
||||
HJustify.Right => UiButton.LabelAlignment.Right,
|
||||
_ => UiButton.LabelAlignment.Center,
|
||||
};
|
||||
// Seed with whatever the child itself authors (typically
|
||||
// blank) so an unbound button doesn't draw stray leftover
|
||||
// text before a controller writes a real value.
|
||||
|
|
@ -1027,6 +1042,60 @@ public static class DatWidgetFactory
|
|||
return button;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// R4-1 (Campaign CC gate round 1 re-test 3): the "Available Skill
|
||||
/// Credits" value overlapped mid-caption ("Available Skill0Credits")
|
||||
/// because <see cref="UiButton.ValueBox"/> was built from the value
|
||||
/// child's RAW authored rect, un-reflowed. Live-DAT probe: the value
|
||||
/// child (<c>0x100002f3</c>) is BASE-INHERITED across four sibling
|
||||
/// buttons of DIFFERING widths — Health/Stamina/Mana at 150px share the
|
||||
/// exact same child id/rect (local X=116) as the wider, 231px Skills
|
||||
/// credits button, and the child's own <c>OriginalParentWidth</c> (the
|
||||
/// design-time parent size baked in at whichever button FIRST resolved
|
||||
/// it — 150, matching Health's own actual width) diverges from Skills
|
||||
/// credits' actual current parent width (231) — exactly the shape
|
||||
/// <see cref="UiLayoutPolicy"/> (retail
|
||||
/// <c>UIElement::UpdateForParentSizeChange @0x00462640</c>, already the
|
||||
/// production raw-edge reflow for live mounted elements via
|
||||
/// <see cref="UiElement.ApplyAnchor"/>) exists to correct. The child's
|
||||
/// own edge modes (Left=2/Right=1, live-DAT-confirmed) are retail's
|
||||
/// "track the far edge as the parent grows" reflow: applying them moves
|
||||
/// the value box from local X=116 to X=197 for Skills credits — landing
|
||||
/// immediately after the caption's own measured end (~x=196,
|
||||
/// <c>SkillsCreditsButton_CaptionFitsFullWidth_ValueChildStartsAtMidpoint</c>)
|
||||
/// instead of colliding mid-caption. Health/Stamina/Mana and the
|
||||
/// Attribute/Credits value child (whose OWN OriginalParentWidth already
|
||||
/// matches their actual parent, or whose edge modes are all 0/fixed)
|
||||
/// reflow to their byte-identical raw rect (deltaX=0 or mode-0 passthrough)
|
||||
/// — this is additive for every already-correct button, not a per-button
|
||||
/// special case.
|
||||
/// </summary>
|
||||
private static (float X, float Y, float Width, float Height) ReflowValueChildRect(
|
||||
ElementInfo child, ElementInfo parent)
|
||||
{
|
||||
float originalParentWidth = child.HasOriginalParentSize ? child.OriginalParentWidth : parent.Width;
|
||||
float originalParentHeight = child.HasOriginalParentSize ? child.OriginalParentHeight : parent.Height;
|
||||
|
||||
var originalChild = UiPixelRect.FromPositionAndSize(
|
||||
(int)child.X, (int)child.Y, (int)child.Width, (int)child.Height);
|
||||
var originalParent = UiPixelRect.FromPositionAndSize(
|
||||
0, 0, (int)originalParentWidth, (int)originalParentHeight);
|
||||
var currentParent = UiPixelRect.FromPositionAndSize(
|
||||
0, 0, (int)parent.Width, (int)parent.Height);
|
||||
// Empty (Width=0/Height=0) "current child" so the static Apply's
|
||||
// currentChild-preservation branch never engages — every axis comes
|
||||
// from the Near/Far formula, matching mode 0's own "keep the raw
|
||||
// authored edge" default for the (frequent) no-anchor case.
|
||||
var noCurrentChild = new UiPixelRect(0, 0, -1, -1);
|
||||
|
||||
UiPixelRect reflowed = UiLayoutPolicy.Apply(
|
||||
child.Left, child.Top, child.Right, child.Bottom,
|
||||
originalChild, originalParent,
|
||||
noCurrentChild, currentParent);
|
||||
|
||||
return (reflowed.X0, reflowed.Y0, reflowed.Width, reflowed.Height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retail UIOption_Checkbox is a UIElement_Button whose visible face is its
|
||||
/// authored indicator child. Its label lives on the option object rather than
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue