fix(chat): round 4 — no user-visible meta text, real /help groups, indicator buttons toggle

Item 3 (#364): every honesty marker is now gone from user-visible /help
text. AllegianceOverview/HouseOverview's "[IMPLEMENTED]" tags and trailing
"Subcommands NOT marked..." sentences, and Day/Log/Render/Motd's appended
"NOT YET IMPLEMENTED in acdream" tails, are removed; the underlying retail
text is corrected/completed against the pseudo-C's own pristine
consolidated data dumps (Log and Motd had been silently truncated; Render
was entirely acdream-authored and is replaced with the real retail usage
string). The three PARTIAL /help group topics (channels/chatting/commands)
are now COMPLETE verbatim listings: HelpStupidChannelHack's three
"vtable slot" operands, previously believed undecodable, are the same
pooled/mislabeled-data artifact this campaign has hit before (AP-113's
precedent) — reading the function's own disassembly for the push imm32
preceding each constructor call resolves all three directly. messagetypes
is now a real ported construction (IsLegalChannel's 14-id whitelist +
LogTextTypeToString's name table + the exact join/wrap format) instead of
an acdream summary. Register row AP-184 retired.

Item 5: the main window's 1/2/3/4 indicator buttons now toggle their
floating chat window on click, per the user's retail memory overruling
the earlier decomp-only reading. UIElement_Button::HandleButtonClick has
its own generic click-driven action dispatch (property 0x12) reaching the
same DoVisibilityToggleAction the Alt+1..4 keybinds use; the button
fixture confirms this half is genuinely armed, but the floating-window
fixture authors no matching listener-registration property, so the
generic mechanism has no proven target in the data on hand. Per
CLAUDE.md, the user's retail memory is the axiom regardless:
ChatWindowController.BindIndicatorClicks wires each indicator's click
through the same ToggleFloatingChatWindow chokepoint the keybinds use,
as explicit user-directed retail behavior. SetIndicatorOpen stays the
sole writer of the Selected mirror so the visual stays consistent
through the click round trip.

Full reconciliation in docs/research/2026-08-09-chat-retail-window-shell.md
§1.4. Campaign plan gets the round-4 findings section; items 1+2
(text-style) are under parallel research, item 4 passed, item 6 deferred
to the settings track.

Suite: 12,579 passed / 4 skipped / 0 failed (Release, complete solution),
up from baseline 12,553/4/0 — net +26 tests, zero regressions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-10 18:46:34 +02:00
parent 8b166f3ea2
commit 5b54387b8e
10 changed files with 1129 additions and 239 deletions

View file

@ -184,19 +184,123 @@ exactly two element ids: `0x1000046f` (max/min, dispatching
(`idMessage == 7`, checked against `this->m_pCCS` / a `0x1000000b` attribute
read). There is no case, anywhere in that function or its base-class fallback
(`ChatInterface::ListenToElementMessage`, called unconditionally at the
function's tail), for `0x10000522``0x10000525`. **Clicking a chat-window
indicator button does NOTHING in retail — reading (a), pure indicator, is
correct; reading (b) is refuted.** The `0x24` input-action-property theory
in reading (b) does not even apply to a mouse click on the button itself: that
property only wires *keyboard* dispatch (`UIElementManager::
DoVisibilityToggleAction` in §1.3), not a button's own `UIElement` click
message, which routes to its LISTENING PARENT — and that parent's handler has
no case for these four ids. acdream ports this exactly: the four indicator
buttons (`ChatWindowController._indicatorButtons`) carry no `OnClick` at all;
`ChatWindowController.SetIndicatorOpen` is their only writer, called only from
`RetailUiRuntime.OnWindowVisibilityChanged` in response to the floating
window's own visibility changing (keybind or otherwise) — a pure one-directional
mirror, matching retail exactly.
function's tail), for `0x10000522``0x10000525`. **This citation is still a
true statement about `gmMainChatUI::ListenToElementMessage` — see the round-4
correction below for why it is the wrong function to have grepped.**
---
**ROUND-4 CORRECTION (2026-08-10) — the user's own retail memory ("clicking
opens/closes the window") overruled the CONCLUSION above, and re-attacking the
question with that as the starting axiom (per CLAUDE.md: the user's retail
memory is the axiom, not a hypothesis to be argued down) found the exact
mechanism the CH6b pass missed.**
The CH6b pass's mistake was scope, not accuracy: it proved
`gmMainChatUI::ListenToElementMessage` has no click case for these ids, then
reasoned "a button's click message… routes to its LISTENING PARENT" and
stopped there. **That's wrong — `UIElement_Button` (Type 1, the class every
one of these four indicators actually is) overrides its OWN click handling
and never asks its parent window first:**
```
UIElement_Button::HandleButtonClick @0x00471E50
00471e65 if (UIElement::GetAttribute_Enum(this, 0x12, &actionId)) // reads its OWN property
00471e72 if (actionId != 1)
00471e95 build an InputEvent(actionId)
00471eb2 ICIDM::GetActionMap()… dispatch through the action map
```
Property `0x12` here is the SAME kind of "input action" enum as `0x24` — not
the parent-window dispatch §1.4's original text assumed didn't apply to
clicks. This IS the generic mechanism a click uses to reach
`UIElementManager::DoVisibilityToggleAction @0x0045B660` (§1.3's own citation,
previously assumed keybind-only) → `BroadcastElementMessage(target, 0x31,
actionId, 0)` for every element registered under that action id via
`RegisterElementForInputAction` (§1.3's property-`0x24` registration —
confirmed as the ONLY call site of `RegisterElementForInputAction` in the
whole binary) → the RECEIVING element's generic base-class handler:
```
UIElement::ListenToElementMessage @0x00462340
00462447 case 8: // idMessage - 0x29 == 8, i.e. raw idMessage 0x31
0046244f GetAttribute_Enum(this, 0x58, &mode) // reads the RECEIVER's OWN property
00462459 if (mode == 1) SetVisible(!currentlyVisible) // toggle
0046245c else if (mode == 2) SetVisible(1) // force-show
0046245f else if (mode == 3) SetVisible(0) // force-hide
```
Neither `gmMainChatUI`, `gmFloatyChatUI`, nor `ChatInterface` overrides
`ListenToElementMessage` for raw idMessage `0x31` (confirmed by reading all
three switches directly — none has a case landing on it), so EVERY window
falls through to this base-class handler unconditionally. This is a complete,
generic, working "click toggles a registered listener's visibility" system —
exactly the "authored button behavior" this reconciliation task hypothesized
— and it is NOT gated on keyboard input the way the original §1.3 text
assumed; `UIElementManager::KeyPressEvent`'s call to `DoVisibilityToggleAction`
is simply ONE caller among the several that can reach it (the button's own
`HandleButtonClick` is another).
**What the authored DATA shows, checked directly against the committed
fixtures:**
- `chat_2100006f.json` — all four indicator elements (`0x10000522`-`0x10000525`)
DO author an Enum-kind property `0x12` (confirmed by `Kind: 0` = Enum in the
fixture's own property dump, matching `LayoutImporter.ConvertProperty`'s
`EnumBaseProperty → UiPropertyKind.Enum` mapping exactly), with values
`0x10000514`-`0x10000517` in element-id order. This is a real, present,
correctly-typed action id — the button-click half of the generic mechanism
is genuinely armed.
- `chat_floaty_2100005b.json` — the floating chat window's own fixture
authors **NO Enum-kind property `0x24` anywhere** (its only hit on property
number 36 decimal is `Kind: 4` = Integer, an unrelated attribute — not the
registration property) and **no property `0x58` at all**. Since
`RegisterElementForInputAction` has exactly one call site in the entire
binary (the property-`0x24` handler in `UIElement::Initialize`; no class
anywhere calls it directly from code), nothing in the shipped floating chat
window LayoutDesc ever registers it as a listener for ANY action id.
`DoVisibilityToggleAction` would look up the button's action id, find zero
registered listeners, and silently return — the click would fire a real
message with no receiver.
- The four action-id VALUES (`0x10000514`-`0x10000517`) are also not
chat-specific: the identical four numbers, in the identical order, are
`gmFriendsUI::PostInit`'s own child-element ids for its Add/Remove/Tell
buttons and friends listbox (`UIElement::GetChildRecursive(this,
0x10000514)` etc., confirmed by direct read). This is almost certainly a
coincidence of Turbine's global per-dat-file asset-id allocator (ids are
assigned client-wide, not scoped per panel), not a cross-reference —
nothing in `gmFriendsUI` registers for input actions either.
**Conclusion: the generic UI action system is real, it exists, and the
buttons genuinely arm their half of it — but the authored DATA available to
us (both committed fixtures, generated from the installed DAT) does not wire
a target for it.** This is consistent with, not a refutation of, the original
CH6b grep of `gmMainChatUI::ListenToElementMessage` — that citation was
looking in the wrong function, but its NEGATIVE RESULT (retail's window-level
message handlers never claim these clicks) still holds; the generic
mechanism, if it does connect the dots in real retail, does so entirely
below the level either grep could see. **Per CLAUDE.md, the user's retail
memory is the axiom regardless: acdream now wires each indicator's click to
toggle its floating window through the SAME `ToggleFloatingChatWindow`
chokepoint the `Alt+1..4` keybinds use
(`ChatWindowController.BindIndicatorClicks`, called by `RetailUiRuntime`
right after mounting the main chat window) — explicitly as USER-DIRECTED
retail behavior, not a claim that the generic-action-system data path has
been proven end-to-end.** `SetIndicatorOpen` stays the ONLY writer of the
indicator's `Selected` mirror (`UiButton.SuppressSelfToggle` stays `true`);
the click drives the real toggle, and the mirror reports the outcome back —
so the visual stays consistent through the round trip even though the write
is now two-way at the FEATURE level.
---
acdream ports this exactly: the four indicator buttons
(`ChatWindowController._indicatorButtons`) now carry a real `OnClick`
(`ChatWindowController.BindIndicatorClicks`, round 4); the mirror half is
unchanged — `ChatWindowController.SetIndicatorOpen` is still the ONLY writer
of `Selected`, still called from `RetailUiRuntime.OnWindowVisibilityChanged`
in response to the floating window's own visibility changing, regardless of
what triggered it (click, keybind, or a restored layout).
### 1.5 Closing a floaty window from its own title bar