fix(chargen): Campaign CC gate round 1 re-test 2 — R3-5/R3-6 color-key swatch/gradient textures

Re-derived gmCGAppearancePage::DoColorSpots @0x0047d850 and DoGradDisk
@0x0047da90: retail does NOT multiply-tint the swatch/grad-circle's
authored sprite. It builds a fresh composited surface once (CreateLocalSurface
+ Blit), then calls SurfaceWindow::ReplaceColor against old-color
RGBAColor(0,0,0,1) (opaque black — the spot template's own placeholder
fill, live-DAT-pixel-confirmed: the 37x44 "spot" resource has a genuine
solid-black CENTER and a genuine non-black RING) — swapping every exact
opaque-black pixel for the swatch's real color while leaving the ring
untouched. A multiply-tint (Batch G's mechanism) is architecturally wrong:
black multiplied by any color stays black (never recolors the center),
and multiplying the ring's own non-black pixels corrupts them — exactly
the reported "we tint the ring" symptom.

Beyond-count swatches (R3-5b) use a COMPLETELY DIFFERENT authored resource
(enum 0x1000000f, "blank" — pixel-confirmed almost no black at all, i.e.
genuinely different art) shown untinted, and retail's own
pColor->SetVisible(1) is unconditional for all 9 swatches (never hidden).
For Eyes (R3-6), DoGradDisk's Eyes branch blits the "grad plug" icon
(enum 0x10000010) untinted, and SetSelection's own Eyes/non-Eyes tail
never hides m_pGradCircle at all — a correction to this port's prior
"_gradCircle.Visible = !isEyes" line.

Ported via a new ChargenColorSpotComposer (CPU-side decode-once + per-color
bake-and-cache-once through the existing TextureCache.UploadRgba8 seam —
the same shape IconComposer.GetSpellComponentIcon already established for
item icons, just matching black instead of white) and a new opt-in
UiButton.ColorKeyFaceResolver / reuse of the existing
UiDatElement.RuntimeImageTexture seam — both additive. Tint keeps its
existing meaning for every reader/test; the grad circle's Tint stays a
genuine multiply for the non-Eyes case (retail's own Blit_Multiply there).
Wired as a fourth late-bound composition seam (SwatchTextureSource), same
pattern/site as the existing three color-computation seams.

Code-complete, unit/live-DAT-tested (including pixel-level proof of the
spot/blank templates' actual content); the user's connected visual gate
is owed — no client launches this batch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-16 17:20:31 +02:00
parent c9313edc63
commit 2886f79f37
7 changed files with 455 additions and 22 deletions

View file

@ -715,6 +715,20 @@ public sealed class RetailUiRuntime : IDisposable
}
}
/// <summary>R3-5/R3-6 (Campaign CC gate round 1 re-test 2): the fourth
/// late-bound seam, same pattern as the three above — see
/// <see cref="AcDream.App.UI.Layout.CharacterCreationAppearancePage.SwatchTextureSource"/>'s
/// own doc comment.</summary>
internal AcDream.App.UI.Layout.IChargenSwatchTextureSource? ChargenSwatchTextureSource
{
get => CharacterCreationController?.AppearanceSwatchTextureSource;
set
{
if (CharacterCreationController is { } controller)
controller.AppearanceSwatchTextureSource = value;
}
}
/// <summary>CC6b-MOUNT: whether the Appearance page (specifically) is
/// the one currently showing — false, safely, before the screen mounts.
/// </summary>