fix(ui): Map tab player/house icon resolution — build the button-swallowed icons from the panel-slot resolve tree, detached from the per-frame layout pass
Two mechanisms, both live-verified (register row AD-108 updated to match): 1. RESOLUTION. The player/house icons (0x100001ED/0x100001EE) are authored as nested dat children of m_pMap (0x100001EC), itself a Type-1 button whose UiButton.ConsumesDatChildren swallows them at build. The old ResolveSwallowedIcon re-imported them standalone via ImportInfos(hostLayout, iconId) — which returns null on the live DAT: FindDesc walks the raw top-level Elements table (one entry for 0x2100006E) and never reaches them. Their ElementInfos only materialize inside the full panel-slot resolve (ImportInfos(0x2100006E, 0x1000018C)) that MountMapHousePanel already imports — the pageInfo Bind already receives. The fix finds each icon's info under m_pMap's own resolved info subtree and BUILDS it through the new Bindings.IconBuilder seam (production: LayoutImporter.Build under the DAT lock — the build half of RowTemplateResolver's shape). An icon the normal walk DID build is preferred (FindDescendant first), so a future ConsumesDatChildren policy change cannot double-build. 2. POSITION. Found by this fix's own F1 live verification: the resolved ring rendered pinned to m_pMap's top-left. PlaceMarker owns marker position outright (retail's gmMapUI::Update re-places every tick; retail's UpdateForParentSizeChange runs only on real parent resize), but acdream re-runs ApplyAnchor per frame and the icon's compatibility anchor had captured the authored (0,0) rect while the panel was still hidden, re-asserting it over PlaceMarker's writes every frame. PrepareIcon now sets Anchors=None (clearing any imported LayoutPolicy), the established runtime-positioned-element convention. Live numeric gate (session character +Acdream, cell 0xF07E003F): independent computation (gid_to_lcoord -> display (90.8E, 0.5S) -> byte-decoded PlaceMarkerOnMap formula, 17x16 icon, marker area (6,8)-(247,258)) predicts local pixel (226,125); the connected client's UI-tree dump shows the icon at screen (1166,195) under m_pMap (940,70) = local (226,125) — exact match in both panel-open dumps. Coordinate text "0.5S,90.8E", Holtburg town-marker tooltip (real-mouse hover), and the House tab's "You may buy another house immediately." sentence all confirmed on screen; ACE-confirmed graceful logout. New pin: MapHousePanelLiveDatMountTests ([InstalledDatFact]) reproduces the production mount recipe against the installed DATs — the test that would have caught this at Batch C: pins the cold-import null, the panel-slot resolution of both icons with non-degenerate extents, AND that PlaceMarker's writes survive the per-frame ApplyAnchor pass. Gates: Release build green; App suite (live-DAT mode) 5479/3 skips (baseline 5478 + the new pin); Runtime 1744/0; full solution green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
38c580ff6d
commit
e316e190cb
6 changed files with 289 additions and 42 deletions
|
|
@ -184,7 +184,8 @@ public sealed class MapPageControllerTests
|
|||
CurrentCalendar: static () => default,
|
||||
PlayerCellId: () => cellId,
|
||||
HousePosition: static () => (CreateObject.ServerPosition?)null,
|
||||
TemplateResolver: (_, e) => new UiText { Width = 10f, Height = 10f, DatElementId = e }),
|
||||
TemplateResolver: (_, e) => new UiText { Width = 10f, Height = 10f, DatElementId = e },
|
||||
IconBuilder: info => new UiText { Width = 10f, Height = 10f, DatElementId = info.Id }),
|
||||
House: new HousePageController.Bindings(Lines: static () => Array.Empty<string>()));
|
||||
|
||||
MapHousePanelController? controller = MapHousePanelController.Bind(rootInfo, layout, callbacks);
|
||||
|
|
@ -218,7 +219,8 @@ public sealed class MapPageControllerTests
|
|||
CurrentCalendar: static () => default,
|
||||
PlayerCellId: () => indoorCellId,
|
||||
HousePosition: static () => (CreateObject.ServerPosition?)null,
|
||||
TemplateResolver: (_, e) => new UiText { Width = 10f, Height = 10f, DatElementId = e }),
|
||||
TemplateResolver: (_, e) => new UiText { Width = 10f, Height = 10f, DatElementId = e },
|
||||
IconBuilder: info => new UiText { Width = 10f, Height = 10f, DatElementId = info.Id }),
|
||||
House: new HousePageController.Bindings(Lines: static () => Array.Empty<string>()));
|
||||
|
||||
MapHousePanelController? controller = MapHousePanelController.Bind(rootInfo, layout, callbacks);
|
||||
|
|
@ -230,12 +232,12 @@ public sealed class MapPageControllerTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void Refresh_PlayerIconTemplateResolutionFails_CoordinateTextStaysEmptyToo()
|
||||
public void Refresh_PlayerIconResolutionFails_CoordinateTextStaysEmptyToo()
|
||||
{
|
||||
// F15 (night-round review): gmMapUI::Update @0x004a2078's gate is
|
||||
// `if (m_pCoordinateText != 0 && m_pPlayerLocationIcon != 0)` — BOTH
|
||||
// widgets present, not "at least one". A player-icon template
|
||||
// resolution failure (leaving _playerIcon null, e.g. a future DAT
|
||||
// widgets present, not "at least one". A player-icon resolution
|
||||
// failure (leaving _playerIcon null, e.g. a future DAT
|
||||
// regression) must skip the coordinate-text write too, not just the
|
||||
// marker placement — the OLD `_coordinateText is null &&
|
||||
// _playerIcon is null` gate only skipped when BOTH were absent, so
|
||||
|
|
@ -252,13 +254,14 @@ public sealed class MapPageControllerTests
|
|||
CurrentCalendar: static () => default,
|
||||
PlayerCellId: () => cellId,
|
||||
HousePosition: static () => (CreateObject.ServerPosition?)null,
|
||||
// Simulates the player icon's own standalone template
|
||||
// resolution failing (ResolveSwallowedIcon's own null path)
|
||||
// while every other swallowed-icon/town-marker resolution
|
||||
// still succeeds normally.
|
||||
TemplateResolver: (_, e) => e == MapPageController.PlayerIconId
|
||||
TemplateResolver: (_, e) => new UiText { Width = 10f, Height = 10f, DatElementId = e },
|
||||
// Simulates the player icon's own build failing
|
||||
// (ResolveSwallowedIcon's null-build path, e.g. a future DAT
|
||||
// regression) while the house icon and every town-marker
|
||||
// resolution still succeed normally.
|
||||
IconBuilder: info => info.Id == MapPageController.PlayerIconId
|
||||
? null
|
||||
: new UiText { Width = 10f, Height = 10f, DatElementId = e }),
|
||||
: new UiText { Width = 10f, Height = 10f, DatElementId = info.Id }),
|
||||
House: new HousePageController.Bindings(Lines: static () => Array.Empty<string>()));
|
||||
|
||||
MapHousePanelController? controller = MapHousePanelController.Bind(rootInfo, layout, callbacks);
|
||||
|
|
@ -283,7 +286,8 @@ public sealed class MapPageControllerTests
|
|||
CurrentCalendar: static () => default,
|
||||
PlayerCellId: static () => 0u,
|
||||
HousePosition: static () => (CreateObject.ServerPosition?)null,
|
||||
TemplateResolver: (_, e) => new UiText { Width = 10f, Height = 10f, DatElementId = e }),
|
||||
TemplateResolver: (_, e) => new UiText { Width = 10f, Height = 10f, DatElementId = e },
|
||||
IconBuilder: info => new UiText { Width = 10f, Height = 10f, DatElementId = info.Id }),
|
||||
House: new HousePageController.Bindings(Lines: static () => Array.Empty<string>()));
|
||||
|
||||
MapHousePanelController? controller = MapHousePanelController.Bind(rootInfo, layout, callbacks);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue