fix(chargen): Campaign CC gate round 1 closeout — Group 1: real color wheel wiring

Lands Batch G's two STOPPED items, making the real palette-color swatch
wheel visually live instead of inert:

- UiButton and UiDatElement gain a per-instance Tint property threaded
  into every existing DrawSprite call (defaults to Vector4.One, so every
  pre-existing button/element is byte-identical unless a caller sets a
  non-identity tint).
- CharacterCreationAppearancePage now sets Tint directly on each color
  swatch button and the GradCircle element, replacing the Batch G
  flat-fill ChargenSwatchColorTile overlay outright — an opaque
  rectangle drawn on top can never reproduce retail's actual
  SurfaceWindow::BlitAndColor(..., Blit_Multiply, color) multiply blend,
  only a genuine per-instance sprite tint can, so the overlay approach is
  deleted rather than layered under the new mechanism.
- CharacterCreationUiController and RetailUiRuntime grow pass-through
  properties (AppearancePalSetSource/AppearanceClothingTableSource/
  AppearancePaletteColorSource) mirroring the existing PreviewControl
  seam, so LivePresentationComposition can wire a DAT-backed
  ChargenAppearanceCatalog into the Appearance page (wiring itself lands
  with the Group 3 commit, since it shares a file with an unrelated F16
  fix).

Register: AP-216/AP-217 RETIRED (161 -> now further reduced in later
commits) — both rows' remaining gaps are closed, not merely narrowed.
CharacterCreationAppearancePageSwatchColorTests updated for the new
Tint-based assertions (two pre-existing assertions were carried over
incorrectly from the old overlay-visibility model and are corrected).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-16 15:33:19 +02:00
parent 1f6365d3e5
commit e1d7d09599
7 changed files with 193 additions and 192 deletions

View file

@ -153,6 +153,19 @@ public sealed class UiButton : UiElement, IUiGlobalTimeListener, IUiDatStateful
/// </summary>
public uint? FaceFileOverride { get; set; }
/// <summary>
/// Campaign CC gate round 1 closeout (Group 1, R2-5): per-instance
/// multiplicative sprite tint, threaded into every <see cref="UiRenderContext.DrawSprite"/>
/// call this class makes (main face, face-segment, drag-acceptance
/// overlay) — retail's own <c>SurfaceWindow::BlitAndColor(...,
/// Blit_Multiply, color)</c>. Default <see cref="Vector4.One"/> (white,
/// full alpha) leaves every DrawSprite call byte-identical to before
/// this property existed; only a caller that explicitly sets a
/// non-identity tint (e.g. <see cref="Layout.CharacterCreationAppearancePage"/>'s
/// color-wheel swatches) changes what draws.
/// </summary>
public Vector4 Tint { get; set; } = Vector4.One;
/// <summary>Additional left inset for left-aligned labels.</summary>
public float LabelOffsetX { get; set; } = 3f;
@ -469,7 +482,7 @@ public sealed class UiButton : UiElement, IUiGlobalTimeListener, IUiDatStateful
float faceWidth = FaceWidth > 0f ? FaceWidth : Width;
float faceHeight = FaceHeight > 0f ? FaceHeight : Height;
ctx.DrawSprite(tex, FaceLeft, FaceTop, faceWidth, faceHeight,
0, 0, faceWidth / tw, faceHeight / th, Vector4.One);
0, 0, faceWidth / tw, faceHeight / th, Tint);
}
}
}
@ -525,7 +538,7 @@ public sealed class UiButton : UiElement, IUiGlobalTimeListener, IUiDatStateful
{
var (tex, _, _) = _resolve(dragSprite);
if (tex != 0)
ctx.DrawSprite(tex, 0f, 0f, Width, Height, 0f, 0f, 1f, 1f, Vector4.One);
ctx.DrawSprite(tex, 0f, 0f, Width, Height, 0f, 0f, 1f, 1f, Tint);
}
}
@ -647,7 +660,7 @@ public sealed class UiButton : UiElement, IUiGlobalTimeListener, IUiDatStateful
// first reflowed by its own four-edge retail layout policy.
ctx.DrawSprite(texture, rect.X0, rect.Y0, rect.Width, rect.Height,
0f, 0f, (float)rect.Width / textureWidth, (float)rect.Height / textureHeight,
Vector4.One);
Tint);
}
private void AddAvailableStates(ElementInfo mediaInfo)