fix(chargen): Campaign CC gate round 1 Batch G — real color wheel (DoColorSpots/DoGradDisk color rendering)
R2-5: retail's gmCGAppearancePage::DoColorSpots/SetSelection/DoGradDisk paint the nine color swatches and the gradient disc with a real, computed representative color (PalSet-averaged for Hair/Nose+Mouth+ Skin/Headgear/Shirt/Trousers/Footwear at fixed sample indices 0xd0/0xb0/0x520; direct-Palette for Eyes at 0x103), not the static authored art acdream showed before this batch. Ports the full palette-to-RGB pipeline: a new pure Core resolver (ChargenSwatchColorResolver + IChargenPaletteColorSource) backed by a new ChargenAppearanceCatalog.TryGetColor reading real Palette dat objects, pinned against the installed EoR dat. CharacterCreationAppearancePage recomputes all nine swatches + the gradient disc's tint on every refresh (part/color/heritage change) and paints them through a new ChargenSwatchColorTile overlay child — a flat-color-fill approximation of retail's actual recolored-sprite blit, since neither UiButton (sealed) nor UiDatElement exposes a per-instance sprite tint today. Two STOPPED items remain outside this batch's file contract before the mechanism is visually live: (1) wiring PalSetSource/ClothingTableSource/ PaletteColorSource from CharacterCreationUiController.cs (mirrors the existing PreviewControl seam); (2) a small additive Tint property on UiButton/UiDatElement for a byte-true recolor instead of the flat fill. Also ports Nose/Mouth/Skin's single non-interactive representative swatch, beyond AP-216/AP-217's original six-part scope. Register AP-216/AP-217 rewritten (not retired — the two STOPPED items keep them open). Tests: 11 new Core, 6 new Content live-DAT, 8 new App-layer fixture. App suite 5321/3 -> 5329/3, Runtime 1735/0 unchanged, zero regressions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
2ad805469d
commit
834c2547a9
10 changed files with 1558 additions and 29 deletions
|
|
@ -396,8 +396,8 @@ AP-94..AP-112 for the confirmed retail-UI completion gaps.
|
|||
| AP-209 | **Filed 2026-08-15 at Campaign CC slice CC3. BRANCH TABLE ADDED at the CC3 review-fix round (F10) — the original filing cited only the ordinary-human enum id, omitting the heritage-dependent branches.** Retail's `classID` wire field is resolved via `DBObj::GetDIDByEnum(...) @ CharGenState::GetCharGenResult 0x005C4030` — a DAT DID category lookup that branches on THREE heritage-dependent enum ids (`0x005C42B5`-`0x005C438B`): `0x10000003` for ordinary heritages, `0x10000090` for Olthoi (heritage `0xc`), `0x10000091` for OlthoiAcid (heritage `0xd`), plus three admin-flag variants of the same three (`0x10000004`/`0x10000092`/`0x10000093`) when the create is admin-flagged. `AcDream.Core` has no DAT/Chorizite dependency (a CC1-established, review-closed constraint), so `RuntimeCharacterCreationState.BuildRequestLocked` sends a constant `0` regardless of heritage. | `src/AcDream.Runtime/Session/RuntimeCharacterCreationState.cs` (`BuildRequestLocked`) | ACE's `PlayerFactory.CreatePlayer` never reads `characterCreateInfo.ClassId` (`references/ACE/Source/ACE.Server/Factories/PlayerFactory.cs:155`, commented out) — the field has no observable server-side effect against the only connected target this campaign gates on. | A future non-ACE server that DOES validate `classID` would reject or misclassify every acdream-created character; a future slice that wires the real DID lookup must NOT default to the ordinary-heritage id for Olthoi/OlthoiAcid characters — this row is the marker (and the branch table) to revisit if that ever becomes a real target. | `CharGenState::GetCharGenResult @ 0x005C4030` (branch table `0x005C42B5`-`0x005C438B`); `DBObj::GetDIDByEnum`; `PlayerFactory.cs:154-155` |
|
||||
| AP-210 | **Filed 2026-08-15 at Campaign CC slice CC3.** Retail's `ApplyTemplate @ 0x005C5080` applies a chosen template's six attributes one at a time through the individually-guarded setters (`SetStrength(this, row.strength, 0)` … `SetSelf(this, row.self, 0)`), each of which can silently refuse to RAISE its value when `GetAbsRemainingCredits` for that specific attribute is exactly zero at the moment it runs — a narrow but real cross-attribute ordering effect when switching heritage/template leaves stale attribute values from a PRIOR selection still resident during the sequential apply. `RuntimeCharacterCreationState.ApplyTemplateLocked` instead assigns `_attributes = row.Attributes` as one atomic replacement. | `src/AcDream.Runtime/Session/RuntimeCharacterCreationState.cs` (`ApplyTemplateLocked`) | Every template row in the installed CharGen DAT is curated, self-consistent data (CC1's installed-DAT gates), so the guard is not expected to trip for any real heritage/template pair in isolation; the ordering effect only matters when switching directly between two heritages/templates with very different attribute totals, which is a corner case not yet gated by a connected test. | A rapid heritage-switch-then-template-switch sequence could theoretically leave an attribute at a value retail's sequential guard would have refused to reach; unreachable through this slice's own commands (heritage selection always re-derives the FULL budget before applying), but a future direct-attribute-manipulation caller bypassing `TrySelectHeritage`/`TrySelectTemplate` could differ from retail. | `CharGenState::ApplyTemplate @ 0x005C5080`; `CharGenState::SetStrength @ 0x005C4660` (representative of all six) |
|
||||
| AP-215 | **Filed 2026-08-15 at Campaign CC slice CC6b-MOUNT (Appearance page visual substitutions); NARROWED 2026-08-16 at the Campaign CC gate round 1 Batch B fix (GF-9) — item 1 (the swatch-selection substitution) RETIRED; RE-NARROWED 2026-08-16 at Batch C fix (GF-6/AP-218) — the "1-based ordinal" framing of item 2 is now STALE and replaced below.** What CLOSED at Batch B: the nine color swatches (`0x1000030f-0x10000317`) now drive the SAME companion overlay elements retail's own `SetColor @0x0047DD50` toggles (`m_tColorWheel[...][0x10][iCurColor*7]->SetVisible`) — `CharacterCreationAppearancePage.RefreshColorAndShadeControls` shows exactly the overlay (`0x10000318-0x10000320`, `SwatchOverlayIds`) at the currently-selected color index and hides the rest. What CLOSED at Batch C: `SetStyleSpinLabel`'s 1-based-ordinal substitution is GONE — `RefreshSpinCaptions` now writes retail's own heritage-flavored STATIC caption (see AP-218, RETIRED). **Still open (RESTATED, not the same gap the ordinal covered):** the four icon-only style spins (hair/eyes/nose/mouth — CC1's `ChargenHairStyle`/`ChargenEyeStrip`/`ChargenFaceStrip` carry only an `IconId`, no name string) now show the SAME static caption regardless of which style is selected — retail's own per-choice visual feedback there is an ICON THUMBNAIL this port still doesn't render (no icon-texture pipeline is wired to ANY chargen widget); the live 3D preview is the player's only feedback for which style is currently active. The four clothing spins (headgear/shirt/trousers/footwear) show a real name via `ChargenGearOption.Name` and have no icon gap. | `src/AcDream.App/UI/Layout/CharacterCreationAppearancePage.cs` (`RefreshColorAndShadeControls`'s overlay loop, CLOSED Batch B; `RefreshSpinCaptions`, static-caption-only, icon gap still open) | An icon-texture pipeline for the four icon-only spins is new UI infrastructure this round's scope doesn't otherwise need; the static caption alone is retail-faithful for the TEXT half. | A pixel-level side-by-side against retail would show no icon thumbnail next to the four icon-only spins' caption (cosmetic gap only — the caption text itself is now byte-correct, and the live 3D preview still shows the actual selection). A future icon-rendering pass (if chargen ever needs one, e.g. for the heritage/template icons too) would naturally close this row. | `ChargenHairStyle`/`ChargenEyeStrip`/`ChargenFaceStrip`/`ChargenGearOption` (CC1, `src/AcDream.Core/CharGen/ChargenAppearanceOptions.cs`) |
|
||||
| AP-216 | **Filed 2026-08-15 at the Campaign CC CC6b-MOUNT review fix round (F2 item 1); PARTIALLY CLOSED 2026-08-16 at the Campaign CC gate round 1 Batch C fix.** Retail's `gmCGAppearancePage::DoColorSpots @0x0047d850` blits each of the nine swatch buttons with the ACTUAL color it represents (computed from the current part's own palette) and blits blank art for any swatch beyond the current part's real color count. **What CLOSED:** the "beyond the count" half — `CharacterCreationAppearancePage.RefreshColorAndShadeControls` now hides (`Visible=false`) any swatch index at or past the current part's own `ColorCount`, the acdream equivalent of retail's blank blit. **Still open:** the "actual color" half — acdream's swatches still show only their authored (static) DAT art regardless of which color they individually represent; painting each swatch with its own computed color needs a PalSet/Palette-id -> RGB resolution pipeline no chargen page currently reads DAT palette pixels through at runtime (new UI infrastructure this batch judged disproportionate to add alongside its ~10 other fixes). | `src/AcDream.App/UI/Layout/CharacterCreationAppearancePage.cs` (`RefreshColorAndShadeControls`'s swatch loop — hides beyond-count swatches, CLOSED; still sets no per-swatch color, OPEN) | The nine swatches already reach the correct SELECTION semantics AND the correct beyond-count visibility through existing `UiButton`/`UiElement.Visible` primitives; painting each swatch with a computed color needs a genuinely new palette-to-RGB render path this batch's scope didn't otherwise need. | A side-by-side against retail shows every VALID swatch drawing the SAME authored art regardless of which color it represents — a cosmetic gap only now (the beyond-count "stuck visibly on" gap that used to mislead a player about how many real choices existed is closed). | `gmCGAppearancePage::DoColorSpots @0x0047d850` |
|
||||
| AP-217 | **Filed 2026-08-15 at the Campaign CC CC6b-MOUNT review fix round (F2 item 4); rewritten 2026-08-15 at the re-review of fix commit `d2a71152` (R3); PARTIALLY CLOSED 2026-08-16 at the Campaign CC gate round 1 Batch C fix.** `gmCGAppearancePage::ListenToElementMessage @0x0047ef30`'s dispatch switch on `idElement - 0x1000030a` has NO `case 4` (present cases: `0`,`1`,`5`-`0xd`,`0x17`,`0x19`-`0x1c`,`0xa5`-`0xa9`,`0xab`-`0xae`) — retail routes NO UI message from the GradCircle (`0x1000030e`, offset `4`) at all; it is not a click target. `DoGradDisk @0x0047da90` is a PAINT-only routine, called from `SetColor` (`@0x0047de18`) and `SetSelection` (`@0x0047e873`/`@0x0047e85d`): it `BlitAndColor`s the gradient graphic with the current part's color and `UIRegion::SetImage`s it onto `m_pGradCircle` (`@0x0047dc9e`/`@0x0047dca9`/`@0x0047dd26`) for every part except Eyes, or blits the blank "grad plug" graphic instead (`@0x0047dcec`, `DoGradDisk(this, 1)`) for Eyes. **What CLOSED:** the Eyes-blank half — `CharacterCreationAppearancePage.RefreshColorAndShadeControls` now hides the GradCircle when the current part is Eyes, the acdream equivalent of the blank "grad plug" blit. **Still open:** the gradient-graphic TINT half — acdream still never repaints the GradCircle with the current part's color; that composite (`Blit_Multiply` against `m_pGradGraphic`/`m_pGradPlug`) needs the SAME palette-to-RGB resolution pipeline AP-216's still-open half needs, so it stays open for the same reason. | `src/AcDream.App/UI/Layout/CharacterCreationAppearancePage.cs` (`RefreshColorAndShadeControls` now hides the GradCircle for Eyes, CLOSED; still never repaints it for any other part, OPEN) | The nine swatch buttons already provide the full, decomp-cited color-selection input path (`SetColor`'s own cases `5`-`0xd`); porting the GradCircle's own gradient-graphic repaint is genuinely new render infrastructure, same as AP-216's open half. | A user in acdream sees the GradCircle stay static instead of visually reflecting the current swatch color for any part OTHER than Eyes (Eyes now correctly blanks) — a cosmetic paint gap, not a dead/unresponsive control; clicking it does nothing in retail either. | `gmCGAppearancePage::ListenToElementMessage @0x0047ef30`; `gmCGAppearancePage::DoGradDisk @0x0047da90`; `gmCGAppearancePage::SetColor @0x0047dd50`; `gmCGAppearancePage::SetSelection @0x0047e260` (calls at `@0x0047e873`/`@0x0047e85d`) |
|
||||
| AP-216 | **Filed 2026-08-15 at the Campaign CC CC6b-MOUNT review fix round (F2 item 1); PARTIALLY CLOSED 2026-08-16 at Batch C (the beyond-count half); REWRITTEN 2026-08-16 at the Campaign CC gate round 1 Batch G fix (R2-5) — the "actual color" half is now IMPLEMENTED AND TESTED, with two narrow STOPPED items outside this batch's file contract before it is visually live.** Retail's `gmCGAppearancePage::DoColorSpots @0x0047d850` blits each of the nine swatch buttons with the ACTUAL color it represents (computed in `SetSelection @0x0047e260` into `m_tColorWheel[i].iRed/iGreen/iBlue` via `ClientCharGenState::GetColorFromPal @0x00563990`, a direct `Palette::get_color32`/`ARGB[index]` read at a fixed per-part sample index — `0xd0` Hair, `0xb0` Nose/Mouth/Skin, `0x103` Eyes, `0x520` Headgear/Shirt/Trousers/Footwear) and blits blank art for any swatch beyond the current part's real color count. **What CLOSED at Batch C:** the "beyond the count" half (`Visible=false` past `ColorCount`). **What Batch G ADDS:** the full palette-to-RGB pipeline — a new pure Core resolver (`ChargenSwatchColorResolver`: PalSet-averaged shape for Hair/Nose+Mouth+Skin/Headgear/Shirt/Trousers/Footwear, direct shape for Eyes, plus the clothing-swatch PalSet lookup through the CURRENTLY EQUIPPED garment's own ClothingTable) backed by a new `ChargenAppearanceCatalog.TryGetColor` (Content) reading real Palette dat objects, pinned against the installed EoR dat (`ChargenAppearanceCatalogColorTests` — e.g. Aluvian male Eye swatch 0 measures RGB(15,63,93), the shared skin PalSet measures a plausible RGB(182,148,118) flesh tone). `CharacterCreationAppearancePage` now computes all nine swatches' colors on every refresh (part change / color change / heritage change — `CharacterCreationAppearancePageSwatchColorTests`) and paints them via a new `ChargenSwatchColorTile` child element added on top of each swatch button. **Two STOPPED items remain, both outside this batch's file contract:** (1) the new `PalSetSource`/`ClothingTableSource`/`PaletteColorSource` late-bound properties (mirroring the existing `PreviewControl` seam) are never assigned by the composition root — until `CharacterCreationUiController.cs` wires a `ChargenAppearanceCatalog` instance into them (a 3-line addition, same shape as the existing `AppearancePreviewControl` wiring), the mechanism stays fully inert and every swatch shows ONLY its authored static art, exactly like before this batch (`UnwiredSources_LeaveEveryTileInvisible` pins this explicitly). (2) `ChargenSwatchColorTile` paints a FLAT color fill (`UiRenderContext.DrawFill`), not a genuine recolored sprite — neither `UiButton` (sealed) nor `UiDatElement` exposes a per-instance `Tint` on its existing `DrawSprite` calls (which DO already carry a `Vector4 tint` parameter the retained-UI shader multiplies against, matching retail's own `Blit_Multiply`); adding one is a small additive change to those two shared widget files this batch does not make. | `src/AcDream.Core/CharGen/ChargenSwatchColor.cs` + `ChargenSwatchColorResolver.cs` (new); `src/AcDream.Content/CharGen/ChargenAppearanceCatalog.cs` (`TryGetColor`, new); `src/AcDream.App/UI/Layout/CharacterCreationAppearancePage.cs` (`ComputeSwatchColors` + the swatch loop, now paints real color OR stays inert without the wiring); `src/AcDream.App/UI/Layout/ChargenSwatchColorTile.cs` (new) | Everything reachable inside this batch's file contract (Core resolver, Content palette read, the App page's own computation + rendering primitive) is fully implemented and tested; the two remaining gaps are BOTH shared-file edits (composition-root wiring; a widget Tint property) outside that contract, reported as STOPPED items rather than worked around. | Until the STOPPED composition-root wiring lands, a user still sees the pre-Batch-G static swatches (this batch changes nothing observable on its own). Once wired, every VALID swatch will show a flat-color patch at its own computed RGB rather than retail's recolored dot-shaped sprite — correct COLOR, approximated SHAPE, until the second STOPPED item (the widget Tint property) also lands. | `gmCGAppearancePage::DoColorSpots @0x0047d850`; `gmCGAppearancePage::SetSelection @0x0047e260`; `ClientCharGenState::GetColorFromPal @0x00563990`; `Palette::get_color32 @0x0053e050`; `CharGenState::StoreColorInformation @0x005c44d0`; `CharGenState::SetHeadgearStyle @0x005c5350` |
|
||||
| AP-217 | **Filed 2026-08-15 at the Campaign CC CC6b-MOUNT review fix round (F2 item 4); rewritten 2026-08-15 (R3); PARTIALLY CLOSED 2026-08-16 at Batch C (the Eyes-blank half); REWRITTEN 2026-08-16 at the Campaign CC gate round 1 Batch G fix (R2-5) — the gradient-TINT half is now IMPLEMENTED AND TESTED, same two STOPPED items as AP-216 (they share the same underlying pipeline and rendering primitive).** `gmCGAppearancePage::ListenToElementMessage @0x0047ef30`'s dispatch switch has NO case for the GradCircle (`0x1000030e`) — it is not a click target. `DoGradDisk @0x0047da90` is PAINT-only, called from `SetColor`'s tail (`@0x0047de18`, AFTER `m_iCurColor` is updated) and from `SetSelection` (`@0x0047e873`/`@0x0047e85d`): it tints the gradient graphic with `m_tColorWheel[m_iCurColor]`'s OWN color for every part except Eyes, or blits the blank "grad plug" for Eyes. **What CLOSED at Batch C:** the Eyes-blank half. **What Batch G ADDS:** the tint half, through the SAME `ChargenSwatchColorResolver`/`ChargenSwatchColorTile` machinery AP-216 now has — `CharacterCreationAppearancePage.RefreshColorAndShadeControls` picks the color at the CURRENTLY SELECTED swatch index (index 0, unconditionally, for Nose/Mouth/Skin — retail hard-codes `eyeColor = 0` for those three cases in `SetSelection`) and paints the GradCircle's own tile with it; Eyes stays permanently untinted (`EyesPart_GradientDiscTileStaysBlank`), and the two STOPPED items from AP-216 (composition-root wiring; a genuine `UiButton`/`UiDatElement` sprite-tint property in place of the current flat-fill approximation) block this half from being visually live for the identical reason. | `src/AcDream.App/UI/Layout/CharacterCreationAppearancePage.cs` (`RefreshColorAndShadeControls`'s GradCircle tint block; `_gradCircleTile`) | Same rationale as AP-216 — the full data pipeline is in place and tested; only the two shared-file STOPPED items (outside this batch's contract) remain before either half renders on screen. | Same STOPPED-item gating as AP-216: no observable change until the composition-root wiring lands; once wired, the disc shows a flat tint rather than retail's recolored gradient graphic until the widget Tint property also lands. | `gmCGAppearancePage::ListenToElementMessage @0x0047ef30`; `gmCGAppearancePage::DoGradDisk @0x0047da90`; `gmCGAppearancePage::SetColor @0x0047dd50`; `gmCGAppearancePage::SetSelection @0x0047e260` (calls at `@0x0047e873`/`@0x0047e85d`) |
|
||||
| AP-219 | **Filed 2026-08-15 at the Campaign CC CC6b-MOUNT review fix round (F2 item 6).** Retail's `gmCGAppearancePage::Update` repositions the Skin spin vertically when Nose/Mouth are hidden, closing the gap those two spins would otherwise leave: `m_pSkinSpin->MoveTo(0, 0x5a)` (Y=90) for Olthoi/OlthoiAcid (`@0x0047edef`) and Gearknight (`@0x0047ea83`), vs `MoveTo(0, 0xb4)` (Y=180) for every other heritage (`@0x0047ec41`). acdream hides Nose/Mouth (`Refresh`'s `clothesHidden` branch) but never repositions Skin, leaving a visible vertical gap in the Face tab's spin list for these three heritages. | `src/AcDream.App/UI/Layout/CharacterCreationAppearancePage.cs` (`Refresh`'s `clothesHidden` branch — hides Nose/Mouth, never moves Skin) | The spins are laid out via their authored LayoutDesc positions (`DatWidgetFactory`), which this campaign's slice doesn't runtime-reposition for any other case; the targeted behavior this round was visibility (hiding unreachable spins), not repositioning the ones that remain. | A side-by-side against retail on Olthoi/OlthoiAcid/Gearknight shows a visible vertical gap where Nose/Mouth used to sit, instead of Skin sliding up to close it — a layout/cosmetic gap, not a functional one. | `gmCGAppearancePage::Update` `MoveTo` calls `@0x0047edef` (Olthoi/OlthoiAcid), `@0x0047ea83` (Gearknight), `@0x0047ec41` (every other heritage, the "normal" position) |
|
||||
| AP-220 | **Filed 2026-08-15 at the Campaign CC CC6b-MOUNT review fix round (F2 item 7); tightened 2026-08-15 at the re-review of fix commit `d2a71152` (N1) — "leaving Gearknight for something else" over-claimed the exit side.** Retail's `gmCGAppearancePage::Update` calls `CharGenState::RandomizeAppearance(state, 0)` + `CharGenState::RandomizeClothing(state, 1)` exactly once, on the SPECIFIC frame the heritage crosses the Gearknight boundary in either direction — entering Gearknight from something else (`@0x0047e973`, gated on `m_LastHeritageGroup != 6`) or leaving Gearknight for a non-Olthoi heritage (`@0x0047eb58`, gated on `m_LastHeritageGroup == 6` inside the `else` arm of the `mHeritageGroup == 0xc || mHeritageGroup == 0xd` Olthoi/OlthoiAcid test `@0x0047eb46` — leaving Gearknight FOR Olthoi or OlthoiAcid takes the Olthoi-specific `if` arm instead and does NOT randomize). acdream's `Refresh` (the `Update` analogue) has no heritage-transition-edge tracking at all and never calls anything on a Gearknight-boundary crossing. | `src/AcDream.App/UI/Layout/CharacterCreationAppearancePage.cs` (`Refresh` — no `_lastHeritageId`-style transition tracking or randomize call) | This is the SAME six-primitive gap AP-212 (the Random button) and AP-214 (ctor-time `RandomizeCharacter`) already track — `RandomizeAppearance`/`RandomizeClothing` are two of AP-212's six named-but-unported `CharGenState` primitives; a THIRD call site for the identical missing primitives doesn't widen the underlying gap, just where it's also reachable. | Switching heritage into or out of Gearknight in acdream leaves the character's prior appearance/clothing selections untouched (whatever indices were already set, now possibly out-of-range and silently clamped by `ConstrainAppearanceByGenderLocked` rather than freshly randomized), where retail re-rolls both — a behavioral gap a connected gate switching heritage to/from Gearknight would observe directly. | `gmCGAppearancePage::Update` `@0x0047e973` (entering Gearknight) and `@0x0047eb58` (leaving Gearknight); `CharGenState::RandomizeAppearance @0x005c4f10`; `CharGenState::RandomizeClothing @0x005c6770` (both already cited by AP-212) |
|
||||
| AP-221 | **Filed 2026-08-15 at the re-review of Campaign CC CC6b-MOUNT fix commit `d2a71152` (R2) — records the F8 one-shot-binding disposition the re-reviewer accepted as a scoped, documented call, but which shipped without a register row of its own. AMENDED at the CC5 review-fix round, F7 (2026-08-16): this row's own "Risk" column named CC5 as the slice that "should close" this gap; CC5 instead DUPLICATED the same one-shot pattern for a second private viewport (the Summary preview) rather than closing it, and the duplicate shipped without extending this row to cover it — corrected below.** The chargen Appearance-page preview's GPU-side renderer/viewport binding in `LivePresentationComposition`'s chargen block reads `RetailUiRuntime.ChargenPreviewViewportWidget` exactly ONCE, synchronously, during the single `GameWindow.OnLoad` composition pass. `ChargenPreviewViewportWidget` is computed-through `CharacterCreationUiMountCoordinator`, which IS explicitly retryable/idempotent — ticked once per frame (via `RetailUiRuntime.Tick`) until its own DAT/resource read succeeds. If the coordinator's synchronous construction-time mount has NOT succeeded by that one composition pass (DATs not readable on that exact frame), the coordinator's later per-frame retries can still restore the rest of the mounted chargen SCREEN, but this GPU-side lease/binding is never retried — the preview stays permanently unbound for the rest of the session: no lease acquired, no renderer assigned to `chargenViewport`, `RetailUiRuntime.ChargenPreviewControl` never set, and the Appearance page's zoom/rotate controls silently no-op for the whole session. The narrowed diagnostic added at R1 (this same commit) is the only operator-visible evidence, and only fires when retained UI is actually mounted. **The Summary preview block (CC5, immediately below the Appearance block in the same method) is the SAME shape against a SECOND independent lease/binding pair (`summaryPreviewLease`/`summaryPreviewController`, `RetailUiRuntime.SummaryPreviewViewportWidget`/`SummaryPreviewControl`) — a DAT/resource miss on that one composition pass leaves the Summary page's 3D preview permanently unbound for the session with only its own narrowed `Console.WriteLine` diagnostic as evidence (no zoom/rotate controls to lose there, since retail's own Summary viewport has none — see `RetailSummaryPreviewPageVisibility`'s doc comment — but the idle-animated preview itself never renders).** | `src/AcDream.App/Composition/LivePresentationComposition.cs` (the chargen preview viewport block, the `if (dispatcherLease.Resource is { } chargenDispatcher && interaction.RetainedUi?.Runtime.ChargenPreviewViewportWidget is { } chargenViewport)` arm and its `else if` diagnostic, plus the Summary preview block's identical `summaryDispatcher`/`SummaryPreviewViewportWidget` arm immediately after it); `src/AcDream.App/UI/RetailUiRuntime.cs` (`ChargenPreviewViewportWidget`, `SummaryPreviewViewportWidget`); `src/AcDream.App/UI/Layout/CharacterCreationUiMountCoordinator.cs` | Retrofitting cross-frame retry into this one binding would mean restructuring the whole composition's one-shot GPU-resource-wiring contract shared by paperdoll (`PaperdollViewportWidget`), creature-appraisal, AND now the Summary preview in the SAME method, plus the fixed `PrivateEntityViewportFrameGroup` array `FrameRootComposition` builds from the result — out of both the CC6b-MOUNT fix round's AND CC5's blast radius; each round accepted the narrower diagnostic-only fix as sufficient, with this row as the tracked follow-up for BOTH bindings now. | On the specific unlucky frame where either coordinator's construction-time `Tick()` has not yet succeeded (a DAT/resource read not ready that frame), a user gets a chargen screen that otherwise mounted fine but whose Appearance 3D preview zoom/rotate controls, OR whose Summary 3D preview entirely, is dead for the ENTIRE session with no visible error beyond the respective narrowed console diagnostic — a session-permanent, hard-to-reproduce loss a future retry-aware rewrite of BOTH bindings should close together (a single fix, not two). | `src/AcDream.App/Composition/LivePresentationComposition.cs:1001-1109` (chargen preview block's own F8 disposition comment) and `:1111-1185` (the Summary preview block, same disposition, referencing this row); `RetailUiRuntime.ChargenPreviewViewportWidget`/`SummaryPreviewViewportWidget`'s doc comments (retry-vs-one-shot contrast) |
|
||||
|
|
|
|||
|
|
@ -37,7 +37,42 @@ acdream GradCircle vs retail's color wheel).
|
|||
disc) where retail shows the gradient wheel + gold swatch dots that
|
||||
CHANGE per selected part — the user's gate PROMOTES AP-216/AP-217's
|
||||
remaining halves (real palette-color swatch rendering + gradient tint)
|
||||
from partial-closed to must-port.
|
||||
from partial-closed to must-port. **CODE-COMPLETE at Batch G (2026-08-16),
|
||||
register AP-216/AP-217 rewritten (not retired — see their own rows):**
|
||||
the retail mechanism (`gmCGAppearancePage::DoColorSpots @0x0047d850` /
|
||||
`SetSelection @0x0047e260` / `DoGradDisk @0x0047da90`) is fully re-derived
|
||||
and ported — a new pure Core resolver
|
||||
(`AcDream.Core.CharGen.ChargenSwatchColorResolver`) computes each of the
|
||||
nine swatches' representative RGB (PalSet-averaged for Hair/Nose+Mouth+
|
||||
Skin/Headgear/Shirt/Trousers/Footwear at retail's own fixed sample
|
||||
indices `0xd0`/`0xb0`/`0x520`, direct-Palette for Eyes at `0x103`) backed
|
||||
by a new `ChargenAppearanceCatalog.TryGetColor` reading real Palette dat
|
||||
objects, pinned against the installed EoR dat
|
||||
(`ChargenAppearanceCatalogColorTests` — e.g. Aluvian male's shared skin
|
||||
PalSet measures a plausible flesh-tone RGB(182,148,118)).
|
||||
`CharacterCreationAppearancePage` recomputes all nine swatches + the
|
||||
gradient disc's tint on every refresh (part change / color change /
|
||||
heritage change, `CharacterCreationAppearancePageSwatchColorTests`), and
|
||||
paints them through a new `ChargenSwatchColorTile` overlay element.
|
||||
**Two STOPPED items block this from being visually live**, both outside
|
||||
Batch G's file contract: (1) the new `PalSetSource`/`ClothingTableSource`/
|
||||
`PaletteColorSource` late-bound seams (mirroring the existing
|
||||
`PreviewControl` pattern) are never assigned by the composition root
|
||||
(`CharacterCreationUiController.cs`) — until wired, the mechanism stays
|
||||
fully inert, matching PRE-Batch-G behavior exactly; (2) the rendering
|
||||
primitive is a flat-color-fill approximation of retail's actual
|
||||
recolored-sprite blit — neither `UiButton` (sealed) nor `UiDatElement`
|
||||
exposes a per-instance sprite `Tint`, though the retained-UI sprite
|
||||
pipeline's `DrawSprite` already carries the `Vector4 tint` multiply
|
||||
retail's own `Blit_Multiply` needs; adding that property is a small,
|
||||
precisely-specified addition to those two shared widget files for the
|
||||
lead to sequence. Nose/Mouth/Skin (retail's own non-interactive single
|
||||
representative swatch, `SetSelection`'s hard-coded `var_1e0 = 1`) is ALSO
|
||||
ported, beyond AP-216/AP-217's original six-part scope. Tests: 11 new
|
||||
Core (`ChargenSwatchColorResolverTests`), 6 new Content live-DAT
|
||||
(`ChargenAppearanceCatalogColorTests`), 8 new App-layer fixture
|
||||
(`CharacterCreationAppearancePageSwatchColorTests`) — App suite
|
||||
5321/3 -> 5329/3, Runtime 1735/0 unchanged, zero regressions.
|
||||
- **R2-6: Town description text misaligned** — R2-1 family.
|
||||
- **R2-7: Summary — (a) text misaligned (R2-1); (b) the summary OVERVIEW
|
||||
listbox is missing its scrollbar; (c) the how-to box's scrollbar
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue