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:
parent
1f6365d3e5
commit
e1d7d09599
7 changed files with 193 additions and 192 deletions
|
|
@ -73,20 +73,29 @@ namespace AcDream.App.UI.Layout;
|
|||
///
|
||||
/// <para>
|
||||
/// <b>The real color wheel (Campaign CC gate round 1 Batch G, R2-5,
|
||||
/// register AP-216/AP-217):</b> retail's <c>DoColorSpots @0x0047d850</c> /
|
||||
/// <c>DoGradDisk @0x0047da90</c> paint each swatch and the gradient disc
|
||||
/// with an ACTUAL representative color sampled from the real DAT palette
|
||||
/// data (<c>AcDream.Core.CharGen.ChargenSwatchColorResolver</c> ports the
|
||||
/// register AP-216/AP-217 — CLOSEOUT (Group 1) makes it visually live):</b>
|
||||
/// retail's <c>DoColorSpots @0x0047d850</c> / <c>DoGradDisk @0x0047da90</c>
|
||||
/// paint each swatch and the gradient disc with an ACTUAL representative
|
||||
/// color sampled from the real DAT palette data
|
||||
/// (<c>AcDream.Core.CharGen.ChargenSwatchColorResolver</c> ports the
|
||||
/// computation — see its own doc for the two color-source shapes and the
|
||||
/// clothing PalSet lookup). <see cref="PalSetSource"/>/
|
||||
/// <see cref="ClothingTableSource"/>/<see cref="PaletteColorSource"/> are
|
||||
/// late-bound composition seams (same pattern as <see cref="PreviewControl"/>)
|
||||
/// a DAT-backed catalog wires in after construction; the <see cref="ChargenSwatchColorTile"/>
|
||||
/// children painted over each swatch/the gradient disc are this batch's
|
||||
/// rendering primitive — see that class's own doc for why it is a flat
|
||||
/// color fill (a documented approximation of retail's actual recolored-
|
||||
/// sprite blit) and the STOPPED shared-file edit that would upgrade it to
|
||||
/// a genuine texture tint.
|
||||
/// clothing PalSet lookup) via a genuine multiplicative sprite tint —
|
||||
/// retail's own <c>SurfaceWindow::BlitAndColor(..., Blit_Multiply,
|
||||
/// color)</c>. <see cref="PalSetSource"/>/<see cref="ClothingTableSource"/>/
|
||||
/// <see cref="PaletteColorSource"/> are late-bound composition seams (same
|
||||
/// pattern as <see cref="PreviewControl"/>) a DAT-backed catalog wires in
|
||||
/// after construction (<c>CharacterCreationUiController.AppearancePalSetSource</c>
|
||||
/// etc., assigned once by <c>LivePresentationComposition</c> alongside the
|
||||
/// existing <c>AppearancePreviewControl</c> wiring). Each swatch
|
||||
/// (<see cref="UiButton"/>) and the gradient disc (<see cref="UiDatElement"/>,
|
||||
/// resolved via <see cref="_gradCircle"/>) now set their OWN
|
||||
/// <see cref="UiButton.Tint"/>/<see cref="UiDatElement.Tint"/> directly —
|
||||
/// the earlier flat-fill <c>ChargenSwatchColorTile</c> overlay (Batch G's
|
||||
/// documented approximation, since neither widget exposed a tint hook yet)
|
||||
/// is retired: a flat opaque rectangle drawn ON TOP of a sprite can never
|
||||
/// reproduce a multiply blend, only a genuine per-instance sprite tint can,
|
||||
/// so this closeout replaces the overlay outright rather than layering a
|
||||
/// tint UNDER it.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
internal sealed class CharacterCreationAppearancePage : IDisposable
|
||||
|
|
@ -193,17 +202,13 @@ internal sealed class CharacterCreationAppearancePage : IDisposable
|
|||
private readonly UiButton? _rotateCounterClockwise;
|
||||
private readonly UiButton? _zoomIn;
|
||||
private readonly UiButton? _zoomOut;
|
||||
private readonly UiElement? _gradCircle;
|
||||
|
||||
/// <summary>R2-5: one flat-color tile per swatch, added as an EXTRA
|
||||
/// child of the swatch it decorates (see <see cref="ChargenSwatchColorTile"/>'s
|
||||
/// own doc) — null wherever the matching <see cref="_swatches"/> entry
|
||||
/// itself is null (nothing to attach to).</summary>
|
||||
private readonly ChargenSwatchColorTile?[] _swatchColorTiles = new ChargenSwatchColorTile?[SwatchIds.Length];
|
||||
|
||||
/// <summary>R2-5: the gradient disc's own tint tile, an extra child of
|
||||
/// <see cref="_gradCircle"/>.</summary>
|
||||
private readonly ChargenSwatchColorTile? _gradCircleTile;
|
||||
/// <summary>The gradient disc (<c>0x1000030e</c>) — Type 3 in the
|
||||
/// authored dat, so <see cref="UiDatElement"/> (not the base
|
||||
/// <see cref="UiElement"/>) is what <see cref="Find{T}"/> resolves;
|
||||
/// typed concretely (post-closeout Group 1) so <see cref="RefreshColorAndShadeControls"/>
|
||||
/// can set <see cref="UiDatElement.Tint"/> directly.</summary>
|
||||
private readonly UiDatElement? _gradCircle;
|
||||
|
||||
private Choice _currentChoice = Choice.Face;
|
||||
private Part _currentPart = Part.Hair;
|
||||
|
|
@ -219,15 +224,13 @@ internal sealed class CharacterCreationAppearancePage : IDisposable
|
|||
/// R2-5 late-bound seams (same pattern as <see cref="PreviewControl"/>
|
||||
/// above) for the real color-wheel mechanism — null (the default)
|
||||
/// leaves every swatch/the gradient disc showing ONLY its authored
|
||||
/// static art, i.e. this page's pre-Batch-G behavior, until a
|
||||
/// composition root supplies a DAT-backed
|
||||
/// <c>AcDream.Content.CharGen.ChargenAppearanceCatalog</c> (which
|
||||
/// already implements all three interfaces) for these three
|
||||
/// properties, mirroring how <see cref="PreviewControl"/> itself gets
|
||||
/// wired in from outside this class. STOPPED (Batch G): that
|
||||
/// assignment is a 3-line addition to
|
||||
/// <c>CharacterCreationUiController.cs</c>, outside this batch's file
|
||||
/// contract — see the batch's handoff notes.
|
||||
/// static art, i.e. this page's pre-Batch-G behavior. Closeout Group 1
|
||||
/// wires a DAT-backed <c>AcDream.Content.CharGen.ChargenAppearanceCatalog</c>
|
||||
/// (which already implements all three interfaces) into these three
|
||||
/// properties via <c>CharacterCreationUiController.AppearancePalSetSource</c>/
|
||||
/// <c>AppearanceClothingTableSource</c>/<c>AppearancePaletteColorSource</c>,
|
||||
/// mirroring how <see cref="PreviewControl"/> itself gets wired in from
|
||||
/// outside this class.
|
||||
/// </summary>
|
||||
internal IChargenPalSetSource? PalSetSource { get; set; }
|
||||
internal IChargenClothingTableSource? ClothingTableSource { get; set; }
|
||||
|
|
@ -280,18 +283,6 @@ internal sealed class CharacterCreationAppearancePage : IDisposable
|
|||
int index = i;
|
||||
swatch.OnClick = () => SelectColor(index);
|
||||
_swatches[i] = swatch;
|
||||
|
||||
// R2-5: an extra CHILD tile, sized to exactly cover the
|
||||
// swatch's own face — see ChargenSwatchColorTile's own doc for
|
||||
// why this is a flat fill rather than a recolored sprite, and
|
||||
// for why ClickThrough there keeps this from ever swallowing
|
||||
// the swatch's own click.
|
||||
var tile = new ChargenSwatchColorTile
|
||||
{
|
||||
Left = 0f, Top = 0f, Width = swatch.Width, Height = swatch.Height,
|
||||
};
|
||||
swatch.AddChild(tile);
|
||||
_swatchColorTiles[i] = tile;
|
||||
}
|
||||
|
||||
for (int i = 0; i < SwatchOverlayIds.Length; i++)
|
||||
|
|
@ -301,15 +292,7 @@ internal sealed class CharacterCreationAppearancePage : IDisposable
|
|||
if (_shadeScroll is not null)
|
||||
_shadeScroll.ScalarChanged = SetShadeFromScalar;
|
||||
|
||||
_gradCircle = Find<UiElement>(pageRoot, GradCircleId);
|
||||
if (_gradCircle is not null)
|
||||
{
|
||||
_gradCircleTile = new ChargenSwatchColorTile
|
||||
{
|
||||
Left = 0f, Top = 0f, Width = _gradCircle.Width, Height = _gradCircle.Height,
|
||||
};
|
||||
_gradCircle.AddChild(_gradCircleTile);
|
||||
}
|
||||
_gradCircle = Find<UiDatElement>(pageRoot, GradCircleId);
|
||||
|
||||
Viewport = Find<UiViewport>(pageRoot, ViewportId);
|
||||
|
||||
|
|
@ -777,12 +760,13 @@ internal sealed class CharacterCreationAppearancePage : IDisposable
|
|||
continue;
|
||||
bool visible = i < displayCount;
|
||||
swatch.Visible = visible;
|
||||
if (_swatchColorTiles[i] is { } tile)
|
||||
{
|
||||
ChargenSwatchRgb? rgb = visible ? swatchColors[i] : null;
|
||||
tile.Color = rgb is { } c ? ToTintColor(c) : null;
|
||||
tile.Visible = rgb is not null;
|
||||
}
|
||||
// Closeout Group 1: a genuine multiplicative sprite tint on the
|
||||
// swatch's own authored spot art (UiButton.Tint), replacing the
|
||||
// Batch G flat-fill overlay. Vector4.One (identity) reproduces
|
||||
// the swatch's bare authored art untouched — both "no color data
|
||||
// yet" (sources unwired) and "beyond this part's color count".
|
||||
ChargenSwatchRgb? rgb = visible ? swatchColors[i] : null;
|
||||
swatch.Tint = rgb is { } c ? ToTintColor(c) : Vector4.One;
|
||||
}
|
||||
|
||||
// AP-217 (Batch C PARTIAL -> Batch G, R2-5, FULL):
|
||||
|
|
@ -798,18 +782,17 @@ internal sealed class CharacterCreationAppearancePage : IDisposable
|
|||
{
|
||||
bool isEyes = _currentPart == Part.Eyes;
|
||||
_gradCircle.Visible = !isEyes;
|
||||
if (_gradCircleTile is { } gradTile)
|
||||
{
|
||||
int gradIndex = isEyes
|
||||
? -1
|
||||
: colorSlot is null
|
||||
? 0
|
||||
: (int)ColorCurrent(_currentPart, snapshot.Appearance);
|
||||
ChargenSwatchRgb? gradColor =
|
||||
gradIndex >= 0 && gradIndex < swatchColors.Length ? swatchColors[gradIndex] : null;
|
||||
gradTile.Color = gradColor is { } gc ? ToTintColor(gc) : null;
|
||||
gradTile.Visible = !isEyes && gradColor is not null;
|
||||
}
|
||||
// Closeout Group 1: same Tint mechanism as the swatches above —
|
||||
// the gradient disc's own authored art is multiplied by the
|
||||
// currently-selected swatch's color instead of an overlay child.
|
||||
int gradIndex = isEyes
|
||||
? -1
|
||||
: colorSlot is null
|
||||
? 0
|
||||
: (int)ColorCurrent(_currentPart, snapshot.Appearance);
|
||||
ChargenSwatchRgb? gradColor =
|
||||
gradIndex >= 0 && gradIndex < swatchColors.Length ? swatchColors[gradIndex] : null;
|
||||
_gradCircle.Tint = gradColor is { } gc ? ToTintColor(gc) : Vector4.One;
|
||||
}
|
||||
|
||||
ChargenShadeSlot? shadeSlot = ShadeSlotFor(_currentPart);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue