docs: file #413 — House tab content (RuntimeHouseState owner + 6 Display* builders)

Precise scope note for the remainder of the House tab wire, per the task's
pre-authorized fallback: RuntimeHouseState owner integration,
DisplayPurchaseTimeText's port (the one builder simple enough to have
landed this session but deferred for time), and the other six Display*
line builders (only exercisable once a house is actually owned).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-17 02:10:35 +02:00
parent dd6c7e09f0
commit 2881af0bfc

View file

@ -24,6 +24,89 @@ What does NOT go here:
- Every session: scan OPEN issues at start; promote/close anything we touched during the session before ending.
- Promoting to a Phase: mark as `DONE (promoted to Phase X)` + commit SHA where the Phase entry landed.
## #413 — House tab shows no content (owned-house display, seven Display* line builders unported)
**Status:** OPEN (filed 2026-08-17, Batch C — overnight hover/UI round, Map/House
toolbar panel).
**What's shipped.** The Map/House panel (host `0x2100006E` slot `0x1000018C`,
`RetailPanelCatalog.MapHouse = 16`) is fully mounted with a working Map tab
(date/time, coordinates, player/house markers, 53 town hotspots with retail
tooltips) and a House tab that mounts correctly with its authored ListBox
(`0x100001E6`) and row template — but the ListBox is genuinely EMPTY, matching
retail's own `gmHouseUI::PostInit @0x004a2710` (it never calls `Update`/
`DisplayHouseData`; the box only populates after a server notice arrives).
The full wire *parsing* groundwork also shipped: outbound HouseQuery
(`0x021E`, `WorldSession.SendHouseQuery`) and inbound parsers for all four
House opcodes (`GameEvents.ParseHouseData`/`ParseHouseStatus`/
`ParseUpdateRentTime`/`ParseUpdateRentPayment`, wired into
`GameEventWiring.WireAll` as optional delegate holes) — all tested
(`HouseEventsTests`, `GameEventWiringTests.WireAll_HouseFamily_ReachesTheirCallbacks`).
**What's missing — three pieces, all deliberately deferred (the task's own
pre-authorized fallback: "land the default-content tab + the enum/parser
groundwork, and file the remainder as a precise ISSUES entry"):**
1. **A `RuntimeHouseState` owner.** The wire delegate holes exist but nothing
consumes them yet — no session-scoped state class holds the parsed
`HouseData`/`HouseStatus`/rent fields, and `HousePageController.Bindings.Lines`/
`OnShown` are unwired defaults (`() => Array.Empty<string>()`, no-op).
Sizing note: a FULL `GameRuntime`-integrated owner (the
`RuntimeTradeState` precedent — construction-transaction `Fault()`
injection point, `GameRuntimeConstructionPoint` enum entry, disposal
ordering, convergence tracking at 2-3 sites) is a substantial standalone
undertaking; judged disproportionate to add alongside the completed Map
tab in one session. A lighter read-only mirror (closer to
`FriendsState`/`SquelchState`'s weight, no full owner ceremony) may be
the right shape — evaluate against the codebase's "single canonical
owner" architecture before choosing.
2. **`gmHouseUI::DisplayPurchaseTimeText @0x004a3110`'s port** — the ONE
builder decomp-confirmed simple enough to land (no `m_pHouseData` early
return; reads the LOCAL PLAYER's own `PropertyInt.HousePurchaseTimestamp`
(`= 199` decimal, confirmed already in `src/AcDream.Core/Properties/PropertyInt.cs:356`)
and `HouseSystem::HasPurchaseWaitPeriodExpired(timestamp) =
(Timer::get_real_time() - timestamp) > 0x278d00` — clean, no FPU noise,
`0x278d00` = 2,592,000 s = 30 days, the house-abandon cooldown). Two fully
recovered literal strings for the expired case: `"You may buy another
house immediately."` (no house owned) / `"...after you abandon this
one."` (owns a house) — these are what a fresh test character (no
`HousePurchaseTimestamp` set, i.e. `0`) would show once queried, since
`HasPurchaseWaitPeriodExpired(0)` is trivially true. The NOT-expired
branch's future-dated wait message uses a `strftime` format string
(`data_7ab7ec`) and a suffix (`". This restriction does not appl…"`)
that BN truncates and this session did not attempt to recover further —
port the expired branch first, mark the not-expired branch's suffix text
as inferred-pending-verification if ported later.
3. **The other six `Display*` line builders** (`DisplayBuyPayment`,
`DisplayRentPayment`, `DisplayBuyTime`, `DisplayRentTimes`,
`DisplayLocation`, `DisplayWarningText` — all called from
`gmHouseUI::DisplayHouseData @0x004a3380`). Each is dozens-to-a-few-hundred
lines of heavily FPU/string-mangled BN pseudo-C (PStringBase sprintf
chains, `HousePaymentList` iteration, `IsPaidInFull`/
`ConstructRentWarningMessage`-style formatting) — genuinely sized as its
own session, and only exercisable once a test character actually owns a
house (not true of `+Acdream` today). `DisplayLocation` is the exception:
its own logic is clean (`GetHouseLocation``LandDefs::gid_to_lcoord`
the SAME `(v-0x400)*0.1+0.5` transform the Map tab already ports via
`RadarCoordinates`) but its output STRING format is BN-mangled the same
way the Map tab's coordinate readout was — reuse whatever resolution
that gets if/when #413's map coordinate format string is independently
recovered.
**Reference:** `docs/research/2026-08-17-map-house-recon.md` (the full
citation set: addresses, ACE cross-references, the two ACE writer stubs for
UpdateRentTime/UpdateRentPayment). `src/AcDream.App/UI/Layout/HousePageController.cs`,
`src/AcDream.Core.Net/Messages/GameEvents.cs` (House parsers),
`src/AcDream.Core.Net/GameEventWiring.cs` (delegate holes).
**Acceptance test once closed:** the House tab, on a fresh `+Acdream` connect
with no owned house, shows "You may buy another house immediately." after
the tab is opened (client sends `HouseQuery`, ACE replies `HouseStatus`,
`RuntimeHouseState` clears `m_pHouseData`-equivalent, `DisplayPurchaseTimeText`'s
expired/no-house branch fires).
## #412 — Options panel Config tab content escapes the window frame (footer mid-panel, rows drawing below the window's bottom edge)
**Status:** DONE 2026-08-16/17 (overnight hover/UI round, Batch A bug 2).