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:
parent
8b166f3ea2
commit
5b54387b8e
10 changed files with 1129 additions and 239 deletions
|
|
@ -76,8 +76,16 @@ public class ChatLayoutConformanceTests
|
|||
[InlineData(0x10000523u)]
|
||||
[InlineData(0x10000524u)]
|
||||
[InlineData(0x10000525u)]
|
||||
public void MountedChatWindow_IndicatorButtons_ImportVisibleAndInert(uint indicatorId)
|
||||
public void MountedChatWindow_IndicatorButtons_BindAloneLeavesClickUnwiredButSelfFlipSuppressed(
|
||||
uint indicatorId)
|
||||
{
|
||||
// Bind() alone (no BindIndicatorClicks call) leaves OnClick unwired —
|
||||
// the click half is a separate opt-in step RetailUiRuntime performs
|
||||
// once its own ToggleFloatingChatWindow method is available (round 4,
|
||||
// 2026-08-10 — see ChatWindowController.SetIndicatorOpen's doc for the
|
||||
// full retail mechanism reconciliation). This test only proves the
|
||||
// Bind-alone state; MountedChatWindow_IndicatorButtons_ClickTogglesWindow
|
||||
// below exercises the full click round trip through BindIndicatorClicks.
|
||||
var infos = FixtureLoader.LoadChatInfos();
|
||||
var layout = LayoutImporter.Build(infos, NoTex, null);
|
||||
var controller = ChatWindowController.Bind(
|
||||
|
|
@ -87,21 +95,18 @@ public class ChatLayoutConformanceTests
|
|||
UiElement? indicator = layout.FindElement(indicatorId);
|
||||
Assert.NotNull(indicator);
|
||||
Assert.True(indicator!.Visible);
|
||||
// Inert: CH6a does not wire a click handler (CH6b's job — the
|
||||
// one-directional visibility mirror has no button-press behavior of
|
||||
// its own in retail either, per the research doc §1.4). UiButton
|
||||
// structurally always reports HandlesClick=true (it can receive the
|
||||
// event), but with no OnClick/OnClickAt bound, a click is a no-op.
|
||||
var button = Assert.IsType<UiButton>(indicator);
|
||||
Assert.Null(button.OnClick);
|
||||
Assert.Null(button.OnClickAt);
|
||||
|
||||
// CH6a/b REJECT-review SHOULD-FIX 3: OnClick==null is the wrong level
|
||||
// to prove "inert" — the fixture's own 0x0B (ToggleBehavior) = true
|
||||
// means a plain press/release would still flip UiButton's internal
|
||||
// Selected mirror even with no OnClick bound. SuppressSelfToggle
|
||||
// (set by ChatWindowController.Bind) is the actual guard; a press
|
||||
// and release must leave Selected unchanged.
|
||||
// to prove "self-flip suppressed" — the fixture's own 0x0B
|
||||
// (ToggleBehavior) = true means a plain press/release would still
|
||||
// flip UiButton's internal Selected mirror even with no OnClick
|
||||
// bound. SuppressSelfToggle (set by ChatWindowController.Bind) is
|
||||
// the actual guard, and it stays true even after round 4 wires a
|
||||
// real click handler — see SetIndicatorOpen's doc for why the blind
|
||||
// self-flip must stay suppressed regardless.
|
||||
Assert.True(button.SuppressSelfToggle);
|
||||
bool before = button.Selected;
|
||||
button.OnEvent(new UiEvent(0, button, UiEventType.MouseDown, Data1: 2, Data2: 2));
|
||||
|
|
@ -109,6 +114,83 @@ public class ChatLayoutConformanceTests
|
|||
Assert.Equal(before, button.Selected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0x10000522u, 1)]
|
||||
[InlineData(0x10000523u, 2)]
|
||||
[InlineData(0x10000524u, 3)]
|
||||
[InlineData(0x10000525u, 4)]
|
||||
public void MountedChatWindow_IndicatorButtons_ClickTogglesWindow_ThroughBindIndicatorClicks(
|
||||
uint indicatorId, int expectedWindowId)
|
||||
{
|
||||
// Round 4 (2026-08-10): clicking an indicator now toggles its floating
|
||||
// chat window through the SAME chokepoint the Alt+1..4 keybinds use
|
||||
// (user-directed retail behavior — see SetIndicatorOpen's doc for the
|
||||
// full mechanism reconciliation: the generic UIElement_Button
|
||||
// action-dispatch system is real and armed on the button side, but
|
||||
// the shipped floating-window fixture authors no matching listener
|
||||
// registration, so this wiring cannot be proven purely from the DAT).
|
||||
var infos = FixtureLoader.LoadChatInfos();
|
||||
var layout = LayoutImporter.Build(infos, NoTex, null);
|
||||
var controller = ChatWindowController.Bind(
|
||||
infos, layout, new ChatVM(new ChatLog()), () => NullCommandBus.Instance, new ChatWindowState(), null, null, NoTex);
|
||||
Assert.NotNull(controller);
|
||||
|
||||
var toggled = new List<int>();
|
||||
controller!.BindIndicatorClicks(windowId =>
|
||||
{
|
||||
toggled.Add(windowId);
|
||||
return true;
|
||||
});
|
||||
|
||||
UiElement? indicator = layout.FindElement(indicatorId);
|
||||
var button = Assert.IsType<UiButton>(indicator);
|
||||
Assert.NotNull(button.OnClick);
|
||||
|
||||
button.OnEvent(new UiEvent(0, button, UiEventType.MouseDown, Data1: 2, Data2: 2));
|
||||
button.OnEvent(new UiEvent(0, button, UiEventType.MouseUp, Data1: 2, Data2: 2));
|
||||
button.OnEvent(new UiEvent(0, button, UiEventType.Click, Data1: 2, Data2: 2));
|
||||
|
||||
Assert.Equal(new[] { expectedWindowId }, toggled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MountedChatWindow_IndicatorButtons_ClickRoundTrip_KeepsMirrorConsistent()
|
||||
{
|
||||
// The full round trip: click -> BindIndicatorClicks' delegate calls the
|
||||
// real toggle -> the caller (standing in for RetailUiRuntime's
|
||||
// WindowVisibilityChanged plumbing) calls SetIndicatorOpen with the
|
||||
// window's NEW state -> Selected matches that new state, not a blind
|
||||
// self-flip. SuppressSelfToggle staying true is what makes this
|
||||
// authoritative-writer invariant hold even though the button now has
|
||||
// a real OnClick.
|
||||
var infos = FixtureLoader.LoadChatInfos();
|
||||
var layout = LayoutImporter.Build(infos, NoTex, null);
|
||||
var controller = ChatWindowController.Bind(
|
||||
infos, layout, new ChatVM(new ChatLog()), () => NullCommandBus.Instance, new ChatWindowState(), null, null, NoTex);
|
||||
Assert.NotNull(controller);
|
||||
|
||||
bool windowOpen = false;
|
||||
controller!.BindIndicatorClicks(windowId =>
|
||||
{
|
||||
windowOpen = !windowOpen;
|
||||
controller.SetIndicatorOpen(windowId, windowOpen);
|
||||
return true;
|
||||
});
|
||||
|
||||
var button = Assert.IsType<UiButton>(layout.FindElement(0x10000522u));
|
||||
Assert.False(button.Selected);
|
||||
|
||||
button.OnEvent(new UiEvent(0, button, UiEventType.MouseDown, Data1: 2, Data2: 2));
|
||||
button.OnEvent(new UiEvent(0, button, UiEventType.MouseUp, Data1: 2, Data2: 2));
|
||||
button.OnEvent(new UiEvent(0, button, UiEventType.Click, Data1: 2, Data2: 2));
|
||||
Assert.True(button.Selected);
|
||||
|
||||
button.OnEvent(new UiEvent(0, button, UiEventType.MouseDown, Data1: 2, Data2: 2));
|
||||
button.OnEvent(new UiEvent(0, button, UiEventType.MouseUp, Data1: 2, Data2: 2));
|
||||
button.OnEvent(new UiEvent(0, button, UiEventType.Click, Data1: 2, Data2: 2));
|
||||
Assert.False(button.Selected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0x10000693u)]
|
||||
[InlineData(0x10000694u)]
|
||||
|
|
|
|||
|
|
@ -79,54 +79,100 @@ public sealed class RetailCommandHelpTableTests
|
|||
RetailCommandHelpTable.AllegiancesGroupDetail);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("channels")]
|
||||
[InlineData("commands")]
|
||||
public void UnresolvedGroups_ContainVerbatimSummaryPlusHonestUnverifiedNote_NoMetaFabrication(
|
||||
string verb)
|
||||
// ── Round 4 (2026-08-10): the 3 PARTIAL groups are now COMPLETE ─────
|
||||
// Closes ISSUES.md #364. No UNVERIFIED note, no meta-fabrication, ever.
|
||||
|
||||
[Fact]
|
||||
public void ChannelsGroup_IsCompleteVerbatimListing_SixBroadcastLines()
|
||||
{
|
||||
Assert.True(RetailCommandHelpTable.TryGetHelpText(verb, out string text));
|
||||
// The old sentinel this round retired -- must never come back.
|
||||
Assert.DoesNotContain("has not yet extracted", text);
|
||||
Assert.Contains("UNVERIFIED", text);
|
||||
Assert.Contains("HelpStupidChannelHack @0x0056f290", text);
|
||||
// ClientCommunicationSystem::HelpStupidChannelHack @0x0056f290,
|
||||
// decoded: "@" + tag + " - Sends a broadcast to your " +
|
||||
// ChannelName + ".\n", read in HelpChannelsGroup's own call order
|
||||
// (a, c, m, p, v, f).
|
||||
Assert.Equal(
|
||||
"@a - Sends a broadcast to your Allegiance.\n"
|
||||
+ "@c - Sends a broadcast to your Co-vassals.\n"
|
||||
+ "@m - Sends a broadcast to your Monarch.\n"
|
||||
+ "@p - Sends a broadcast to your Patron.\n"
|
||||
+ "@v - Sends a broadcast to your Vassals.\n"
|
||||
+ "@f - Sends a broadcast to your Fellowship.\n",
|
||||
RetailCommandHelpTable.ChannelsGroupDetail);
|
||||
|
||||
Assert.True(RetailCommandHelpTable.TryGetHelpText("channels", out string viaLookup));
|
||||
Assert.Equal(RetailCommandHelpTable.ChannelsGroupDetail, viaLookup);
|
||||
Assert.DoesNotContain("UNVERIFIED", viaLookup);
|
||||
Assert.DoesNotContain("not yet extracted", viaLookup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChannelsGroup_SummaryLineIsVerbatim()
|
||||
public void ChattingGroup_IsCompleteVerbatimListing_ThirteenLines()
|
||||
{
|
||||
Assert.True(RetailCommandHelpTable.TryGetHelpText("channels", out string text));
|
||||
Assert.StartsWith(
|
||||
"@help channels - How to communicate with people in your allegiance or fellowship.",
|
||||
text);
|
||||
// HelpChattingGroup's Detail branch, exact source order: chat,
|
||||
// notell, HelpReply(Summary) [reply+pr+mr], retell, say, tell, the
|
||||
// same 6-line broadcast block as ChannelsGroupDetail, afk. The
|
||||
// "@say" line's own retail literal has NO trailing newline
|
||||
// (confirmed by direct byte sweep) -- retail's own output runs it
|
||||
// straight into "@tell"'s line with no line break; ported as found.
|
||||
Assert.Equal(
|
||||
"@chat - Sets whether or not you receive normal chat.\n"
|
||||
+ "@notell - Sets whether or not you receive @tell's.\n"
|
||||
+ "@reply - Sends some text to the last person who @tell'd you.\n"
|
||||
+ "@pr - Sends some text to the last person who @p'd you.\n"
|
||||
+ "@mr - Sends some text to the last person who @m'd you.\n"
|
||||
+ "@retell - Sends some text to the last person you @tell'd.\n"
|
||||
+ "@say - Says some text to everyone around you."
|
||||
+ "@tell - Sends a private message to another character.\n"
|
||||
+ "@a - Sends a broadcast to your Allegiance.\n"
|
||||
+ "@c - Sends a broadcast to your Co-vassals.\n"
|
||||
+ "@m - Sends a broadcast to your Monarch.\n"
|
||||
+ "@p - Sends a broadcast to your Patron.\n"
|
||||
+ "@v - Sends a broadcast to your Vassals.\n"
|
||||
+ "@f - Sends a broadcast to your Fellowship.\n"
|
||||
+ "@afk - Set your away-from-keyboard status.\n",
|
||||
RetailCommandHelpTable.ChattingGroupDetail);
|
||||
|
||||
Assert.True(RetailCommandHelpTable.TryGetHelpText("chatting", out string viaLookup));
|
||||
Assert.Equal(RetailCommandHelpTable.ChattingGroupDetail, viaLookup);
|
||||
Assert.DoesNotContain("UNVERIFIED", viaLookup);
|
||||
Assert.DoesNotContain("not yet extracted verbatim", viaLookup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChattingGroup_SevenOfEightEntriesAreVerbatim_FifthEntryMarkedUnverified()
|
||||
public void CommandsGroup_IsCompleteVerbatimListing_ConcatenatesEveryOtherGroup()
|
||||
{
|
||||
Assert.True(RetailCommandHelpTable.TryGetHelpText("chatting", out string text));
|
||||
Assert.Contains(
|
||||
"@chat - Sets whether or not you receive normal chat.\n",
|
||||
text);
|
||||
Assert.Contains(
|
||||
"@notell - Sets whether or not you receive @tell's.\n",
|
||||
text);
|
||||
Assert.Contains(
|
||||
"@reply - Sends some text to the last person who @tell'd you.\n",
|
||||
text);
|
||||
Assert.Contains(
|
||||
"@retell - Sends some text to the last person you @tell'd.\n",
|
||||
text);
|
||||
Assert.Contains(
|
||||
"@say - Says some text to everyone around you.\n",
|
||||
text);
|
||||
Assert.Contains(
|
||||
"@tell - Sends a private message to another character.\n",
|
||||
text);
|
||||
Assert.Contains(
|
||||
"@afk - Set your away-from-keyboard status.\n",
|
||||
text);
|
||||
Assert.Contains("HelpStupidChannelHack @0x0056f290", text);
|
||||
// HelpAllGroup's Detail branch: HelpAllegiancesGroup(Detail), the
|
||||
// same 6-line broadcast block, HelpChattingGroup(Detail),
|
||||
// HelpDeathGroup(Detail), emote+emotes, fillcomps, saveui(short),
|
||||
// loadui(short), saveui(short) AGAIN, loadui(short) AGAIN
|
||||
// (a CONFIRMED retail duplicate), lockui, friends(short), the
|
||||
// house one-liner, squelch+unsquelch+messagetypes, then
|
||||
// HelpStatusGroup(Detail), HelpTextGroup(Detail).
|
||||
string expected =
|
||||
RetailCommandHelpTable.AllegiancesGroupDetail
|
||||
+ RetailCommandHelpTable.ChannelsGroupDetail
|
||||
+ RetailCommandHelpTable.ChattingGroupDetail
|
||||
+ RetailCommandHelpTable.DeathGroupDetail
|
||||
+ "@emote - Performs a text emote.\n@emotes - Lists all standard emotes.\n"
|
||||
+ "@fillcomps - Helps you buy components in bulk.\n"
|
||||
+ "@saveui <filename> - Saves the current user interface.\n"
|
||||
+ "@loadui <filename> - Loads a previously saved user interface.\n"
|
||||
+ "@saveui <filename> - Saves the current user interface.\n"
|
||||
+ "@loadui <filename> - Loads a previously saved user interface.\n"
|
||||
+ "@lockui - Toggles the locked state of the UI layout.\n"
|
||||
+ "@friends - Helps you manage your friends list.\n"
|
||||
+ "@house - Commands that help you manage your house, including guest and storage management.\n"
|
||||
+ "@squelch - Squelches a character or account.\n"
|
||||
+ "@unsquelch - Unsquelches a squelched character or account.\n"
|
||||
+ "@messagetypes - Lists all types of messages that can be squelched or filtered.\n"
|
||||
+ RetailCommandHelpTable.StatusGroupDetail
|
||||
+ RetailCommandHelpTable.TextGroupDetail;
|
||||
|
||||
Assert.Equal(RetailCommandHelpTable.CommandsGroupDetail, expected);
|
||||
|
||||
Assert.True(RetailCommandHelpTable.TryGetHelpText("commands", out string viaLookup));
|
||||
Assert.Equal(RetailCommandHelpTable.CommandsGroupDetail, viaLookup);
|
||||
Assert.DoesNotContain("UNVERIFIED", viaLookup);
|
||||
Assert.DoesNotContain("not yet extracted", viaLookup);
|
||||
}
|
||||
|
||||
// Campaign CH user-gate round 2, item 3: 7 of the ~35 channel
|
||||
|
|
@ -425,10 +471,178 @@ public sealed class RetailCommandHelpTableTests
|
|||
// count separately here since they are DISTINCT Definitions with
|
||||
// their own summary text, even though several share ONE extracted
|
||||
// Detail text -- see the CmdHashData note in RetailCommandHelpTable's
|
||||
// class remarks).
|
||||
Assert.Equal(42, extractedCount);
|
||||
// class remarks). Round 4 (2026-08-10): messagetypes moved from
|
||||
// unverified to extracted (its real live-construction is now
|
||||
// ported -- see RetailCommandHelpTable.MessageTypesDetail) --
|
||||
// 43/4/0, zero remaining unverified leaf verbs.
|
||||
Assert.Equal(43, extractedCount);
|
||||
Assert.Equal(4, confirmedNullCount);
|
||||
Assert.Equal(1, unverifiedCount);
|
||||
Assert.Equal(0, unverifiedCount);
|
||||
}
|
||||
|
||||
// ── Round 4 (2026-08-10): no user-visible meta-markers, anywhere ────
|
||||
// Honesty markers ("IMPLEMENTED", "NOT YET IMPLEMENTED", "UNVERIFIED",
|
||||
// "has not yet extracted") belong in code comments/docs/the divergence
|
||||
// register per CLAUDE.md -- never in a string a player can read.
|
||||
|
||||
public static IEnumerable<object[]> AllUserVisibleStrings()
|
||||
{
|
||||
yield return new object[] { nameof(RetailCommandHelpTable.Day), RetailCommandHelpTable.Day };
|
||||
yield return new object[] { nameof(RetailCommandHelpTable.Log), RetailCommandHelpTable.Log };
|
||||
yield return new object[] { nameof(RetailCommandHelpTable.Render), RetailCommandHelpTable.Render };
|
||||
yield return new object[] { nameof(RetailCommandHelpTable.Motd), RetailCommandHelpTable.Motd };
|
||||
yield return new object[]
|
||||
{
|
||||
nameof(RetailCommandHelpTable.AllegianceOverview), RetailCommandHelpTable.AllegianceOverview,
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
nameof(RetailCommandHelpTable.HouseOverview), RetailCommandHelpTable.HouseOverview,
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
nameof(RetailCommandHelpTable.ChannelsGroupDetail), RetailCommandHelpTable.ChannelsGroupDetail,
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
nameof(RetailCommandHelpTable.ChattingGroupDetail), RetailCommandHelpTable.ChattingGroupDetail,
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
nameof(RetailCommandHelpTable.CommandsGroupDetail), RetailCommandHelpTable.CommandsGroupDetail,
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
nameof(RetailCommandHelpTable.MessageTypesDetail), RetailCommandHelpTable.MessageTypesDetail,
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
nameof(RetailCommandHelpTable.AvailableHelpListing), RetailCommandHelpTable.AvailableHelpListing,
|
||||
};
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(AllUserVisibleStrings))]
|
||||
public void UserVisibleString_CarriesNoHonestyMarker(string _, string text)
|
||||
{
|
||||
Assert.DoesNotContain("IMPLEMENTED", text);
|
||||
Assert.DoesNotContain("UNVERIFIED", text);
|
||||
Assert.DoesNotContain("has not yet extracted", text);
|
||||
Assert.DoesNotContain("acdream has not", text);
|
||||
Assert.DoesNotContain("not yet extracted", text);
|
||||
Assert.DoesNotContain("NOT YET", text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AllegianceOverview_NoBracketAnnotations_MatchesPristineRetailData()
|
||||
{
|
||||
// Read directly off data_7dfb58's own consolidated dump
|
||||
// (acclient_2013_pseudo_c.txt:1031210-1031230) -- BN already fully
|
||||
// decodes this block, no vtable-slot artifact to work around.
|
||||
Assert.Contains("Also: @ab\n", RetailCommandHelpTable.AllegianceOverview);
|
||||
Assert.DoesNotContain("[IMPLEMENTED", RetailCommandHelpTable.AllegianceOverview);
|
||||
Assert.Contains(
|
||||
"@allegiance hometown - Recalls you to your allegiance bindstone, if your allegiance has tied to one.\n",
|
||||
RetailCommandHelpTable.AllegianceOverview);
|
||||
Assert.DoesNotContain("Subcommands NOT marked", RetailCommandHelpTable.AllegianceOverview);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HouseOverview_NoBracketAnnotations_MatchesPristineRetailData()
|
||||
{
|
||||
// Read directly off data_7dd3d0's own consolidated dump
|
||||
// (acclient_2013_pseudo_c.txt:1031017-1031037).
|
||||
Assert.Contains(
|
||||
"@house available - See @hslist\n",
|
||||
RetailCommandHelpTable.HouseOverview);
|
||||
Assert.Contains(
|
||||
"@house recall - Teleports you to your house.\n",
|
||||
RetailCommandHelpTable.HouseOverview);
|
||||
Assert.DoesNotContain("[IMPLEMENTED", RetailCommandHelpTable.HouseOverview);
|
||||
Assert.DoesNotContain("Subcommands NOT marked", RetailCommandHelpTable.HouseOverview);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Log_IsCompleteThreeParagraphRetailText()
|
||||
{
|
||||
// char const data_7df7f8[0x2ca] -- the prior extraction had
|
||||
// silently dropped the middle sentence and both trailing
|
||||
// paragraphs in addition to carrying the meta tail.
|
||||
Assert.Equal(
|
||||
"@log <name> - Echoes chat text to a logfile. All the information that appears in your chat window after you type this command will be copied into a text file. Choose the file you are copying to by naming it in the command. If this file already exists, it will add the additional text to the end of it. To turn off logging, simply retype @log.\n"
|
||||
+ "@log AClog.txt - Echoes chat text to a log file named Aclog.txt in your Asheron's Call directory. After you use this command, all the information that appears in your chat window will be written to a file in your Asheron's Call directory named Aclog.txt.\n"
|
||||
+ "@log - If you are currently copying the text in your chat window to a logfile, this command will stop the process.\n",
|
||||
RetailCommandHelpTable.Log);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Render_IsRealRetailUsageText_NotAcdreamDescription()
|
||||
{
|
||||
// char const render_option_usage[0x98] @ 0x0079B878 -- the prior
|
||||
// text ("Forwards to the client's render-option surface…") was
|
||||
// entirely acdream-authored; this is the real retail literal.
|
||||
Assert.Equal(
|
||||
"Usage:\n"
|
||||
+ "@render <option> <value>\n"
|
||||
+ " radius # : set landscape radius (between 5 and 25)\n"
|
||||
+ " fov # : set field of view (between 10 and 160)\n",
|
||||
RetailCommandHelpTable.Render);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Motd_IsCompleteThreeLineRetailText_PreservesRetailTypo()
|
||||
{
|
||||
// char const data_7da300[0xdb] -- retail's own "clear-" (no space
|
||||
// before the dash) is byte-confirmed, not an acdream typo.
|
||||
Assert.Equal(
|
||||
"@allegiance motd - Displays the message of the day for your allegiance.\n"
|
||||
+ "@allegiance motd set <text> - Sets the MOTD. Can only be used by monarchs.\n"
|
||||
+ "@allegiance motd clear- Clears the MOTD. Can only be used by monarchs.\n",
|
||||
RetailCommandHelpTable.Motd);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MessageTypesDetail_IsRealConstructionFromLegalChannelWhitelist()
|
||||
{
|
||||
// LogTextTypeEnumMapper::IsLegalChannel @0x006AFF40 (14-id
|
||||
// whitelist) + LogTextTypeEnumMapper::LogTextTypeToString
|
||||
// @0x006AFF90 (per-id name) + the ", " join (data_0x794334) +
|
||||
// GetListofSquelchChannels' own "Squelch channels are as
|
||||
// follows:\n %s\n" wrap (data_0x7e2fb8), all swept/read directly.
|
||||
Assert.Equal(
|
||||
"Squelch channels are as follows:\n"
|
||||
+ " Speech, Tell, Combat, Magic, Emote, Appraisal, Spellcasting, Allegiance, Fellowship, Combat_Enemy, Combat_Self, Recall, Craft, Salvaging\n",
|
||||
RetailCommandHelpTable.MessageTypesDetail);
|
||||
|
||||
Assert.True(RetailCommandHelpTable.TryGetCatalogVerbDetailText(
|
||||
"messagetypes", out string messagetypes));
|
||||
Assert.Equal(RetailCommandHelpTable.MessageTypesDetail, messagetypes);
|
||||
|
||||
foreach (string alias in new[] { "message_types", "msgtypes", "msg_types" })
|
||||
{
|
||||
Assert.True(RetailCommandHelpTable.TryGetCatalogVerbDetailText(alias, out string aliasText));
|
||||
Assert.Equal(RetailCommandHelpTable.MessageTypesDetail, aliasText);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HelpMessageTypes_ThroughRouter_PrintsRetailsExactDetailText()
|
||||
{
|
||||
var log = new AcDream.Core.Chat.ChatLog();
|
||||
var vm = new ChatVM(log, displayLimit: 50);
|
||||
var bus = new RecordingCommandBus();
|
||||
|
||||
var outcome = ChatCommandRouter.Submit(
|
||||
"/help messagetypes", vm, bus, ChatChannelKind.Say);
|
||||
|
||||
Assert.Equal(SubmitOutcome.ClientHandled, outcome);
|
||||
var entries = log.Snapshot();
|
||||
Assert.Equal(2, entries.Length);
|
||||
Assert.Equal(RetailCommandHelpTable.HelpPrefixNote, entries[0].Text);
|
||||
Assert.Equal(
|
||||
RetailCommandHelpTable.ForMoreInformationPrefix
|
||||
+ RetailCommandHelpTable.MessageTypesDetail,
|
||||
entries[1].Text);
|
||||
}
|
||||
|
||||
private sealed class RecordingCommandBus : ICommandBus
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue