feat(chat): the talk-focus menu's Tell-to / Squelch entries actually work

Both entries were deliberate no-ops — the code said so — and both showed a
static label where retail shows the selected player's NAME.

Retail builds them in gmMainChatUI::InitTalkFocusMenu @0x004CDC50 and rebuilds
their labels every time the menu opens, substituting the selection through
StringInfo::AddVariable_String (@0x004CD91C / @0x004CD982). So they now read
"Tell to Dww" / "Squelch (ignore) Dww", rebuilt on open from a live selection
provider, and grey out with nothing selected — retail arms the tell slot only
for a talkable target (SetTalkFocusEnabled(2, 1) @0x004CD9B0).

Picking "Tell to X" aims the chat bar at X. That needed one piece of plumbing:
the parser's plain-speech fallthrough returned a null target, so a line typed
under a Tell focus was dropped by the router for having no one to send to.
Parse/Submit now carry an optional default tell target for exactly that case.

"Squelch X" publishes the ALREADY-REGISTERED /squelch verb rather than
reimplementing the request — the ModifyCharacterSquelch wire builder
(CM_Communication::Event_ModifyCharacterSquelch @0x006A42D0) has been there all
along; only the menu path to it was missing.

UiMenu gains an OnOpen seam, because a menu whose Items are fixed at Bind can
only ever say "Tell to Selected". It fires before _open flips so the rebuilt
rows are measured and drawn in the same opening.

Solution builds clean; 14,480 tests pass on the standard hermetic lane filter,
0 failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-21 06:23:46 +02:00
parent 10304f6dc2
commit 581a61ef0c
6 changed files with 209 additions and 17 deletions

View file

@ -1525,7 +1525,17 @@ public sealed class RetailUiRuntime : IDisposable
_bindings.Chat.Windows,
_bindings.Assets.DefaultFont,
_bindings.Assets.DebugFont,
_bindings.Assets.ResolveSprite);
_bindings.Assets.ResolveSprite,
// The talk-focus menu's "Tell to X" / "Squelch (ignore) X" act on
// the current selection and carry its name.
selectedTargetName: () =>
{
uint selected = _bindings.Toolbar.Selection.SelectedObjectId ?? 0u;
if (selected == 0u)
return null;
string? name = _bindings.Toolbar.ResolveName(selected);
return string.IsNullOrWhiteSpace(name) ? null : name;
});
if (controller is null)
{
Console.WriteLine("[D.2b] chat: required role elements missing in 0x2100006F.");