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:
parent
10304f6dc2
commit
581a61ef0c
6 changed files with 209 additions and 17 deletions
|
|
@ -5,6 +5,7 @@ using AcDream.App.UI.Layout;
|
|||
using AcDream.Core.Chat;
|
||||
using AcDream.UI.Abstractions;
|
||||
using AcDream.UI.Abstractions.Panels.Chat;
|
||||
using AcDream.Runtime.Chat;
|
||||
|
||||
namespace AcDream.App.Tests.UI.Layout;
|
||||
|
||||
|
|
@ -157,6 +158,76 @@ public class ChatWindowControllerTests
|
|||
Assert.NotNull(ctrl);
|
||||
}
|
||||
|
||||
// ── Talk-focus specials: "Tell to X" / "Squelch (ignore) X" ─────────────
|
||||
|
||||
/// <summary>
|
||||
/// Both entries act on the CURRENT SELECTION and carry its name, and both
|
||||
/// used to be deliberate no-ops.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void TalkFocusSpecials_CarryTheSelectedName_AndActOnIt()
|
||||
{
|
||||
var (rootInfo, layout, vm) = BuildTestTree();
|
||||
var bus = new CaptureBus();
|
||||
string? selected = "Dww";
|
||||
|
||||
ChatWindowController? ctrl = ChatWindowController.Bind(
|
||||
rootInfo, layout, vm, () => bus, new ChatWindowState(), null, null, NoTex,
|
||||
selectedTargetName: () => selected);
|
||||
Assert.NotNull(ctrl);
|
||||
UiMenu menu = Assert.IsType<UiMenu>(layout.FindElement(0x10000014u));
|
||||
|
||||
// Retail substitutes the name into both labels
|
||||
// (StringInfo::AddVariable_String @0x004CD91C / @0x004CD982).
|
||||
menu.OnOpen!.Invoke();
|
||||
Assert.Equal("Squelch (ignore) Dww", menu.Items[0].Label);
|
||||
Assert.Equal("Tell to Dww", menu.Items[1].Label);
|
||||
|
||||
// Picking "Tell to Dww" aims the chat bar at them, so an ordinary typed
|
||||
// line is sent as a tell rather than being dropped for want of a target.
|
||||
menu.OnSelect!.Invoke(menu.Items[1].Payload);
|
||||
ctrl!.Input.OnSubmit!.Invoke("hello");
|
||||
SendChatCmd tell = Assert.IsType<SendChatCmd>(Assert.Single(bus.Published));
|
||||
Assert.Equal(ChatChannelKind.Tell, tell.Channel);
|
||||
Assert.Equal("Dww", tell.TargetName);
|
||||
Assert.Equal("hello", tell.Text);
|
||||
|
||||
// Squelch reaches the already-registered /squelch verb rather than
|
||||
// reimplementing the request.
|
||||
bus.Published.Clear();
|
||||
menu.OnSelect.Invoke(menu.Items[0].Payload);
|
||||
ExecuteClientCommandCmd squelch =
|
||||
Assert.IsType<ExecuteClientCommandCmd>(Assert.Single(bus.Published));
|
||||
Assert.Equal(ClientCommandId.Squelch, squelch.Command);
|
||||
Assert.Equal("Dww", squelch.Arguments);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TalkFocusSpecials_AreInertAndUnnamedWithNothingSelected()
|
||||
{
|
||||
var (rootInfo, layout, vm) = BuildTestTree();
|
||||
var bus = new CaptureBus();
|
||||
|
||||
ChatWindowController? ctrl = ChatWindowController.Bind(
|
||||
rootInfo, layout, vm, () => bus, new ChatWindowState(), null, null, NoTex,
|
||||
selectedTargetName: () => null);
|
||||
Assert.NotNull(ctrl);
|
||||
UiMenu menu = Assert.IsType<UiMenu>(layout.FindElement(0x10000014u));
|
||||
|
||||
menu.OnOpen!.Invoke();
|
||||
Assert.Equal("Squelch (ignore)", menu.Items[0].Label);
|
||||
Assert.Equal("Tell to Selected", menu.Items[1].Label);
|
||||
|
||||
// Retail arms the tell slot only once a talkable object is selected
|
||||
// (SetTalkFocusEnabled(2, 1) @0x004CD9B0).
|
||||
Assert.False(menu.EnabledProvider!(menu.Items[0].Payload));
|
||||
Assert.False(menu.EnabledProvider(menu.Items[1].Payload));
|
||||
|
||||
menu.OnSelect!.Invoke(menu.Items[1].Payload);
|
||||
menu.OnSelect.Invoke(menu.Items[0].Payload);
|
||||
Assert.Empty(bus.Published);
|
||||
}
|
||||
|
||||
// ── Test 2: Transcript is placed as a child of the transcript panel ──────
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue