1139 lines
78 KiB
C#
1139 lines
78 KiB
C#
using System.Collections.Frozen;
|
|
using AcDream.Core.Chat;
|
|
|
|
namespace AcDream.Runtime.Chat;
|
|
|
|
/// <summary>
|
|
/// Campaign CH slice CH4 (2026-08-09): <c>/help <verb></c> text for
|
|
/// every retail-registry verb — originally chat aliases and channel verbs
|
|
/// (<see cref="ChatInputParser"/>), the null-func help-only nodes (retail
|
|
/// registers these with NO handler; typing them bare reaches the server,
|
|
/// only <c>@help <verb></c> shows anything locally), and the
|
|
/// allegiance/house command overviews (the per-subcommand detail lives
|
|
/// here too). <b>Corrected at the consolidated-review round
|
|
/// (2026-08-10), SHOULD-FIX 1:</b> the sentence above previously claimed
|
|
/// this table covers only verbs <see cref="RetailClientCommandCatalog"/>
|
|
/// "doesn't dispatch directly" — that framing is now FALSE and was itself
|
|
/// an overclaim-by-omission: <see cref="CatalogVerbDetailByVerb"/> (see
|
|
/// its own remarks, and the "Catalog leaf-verb Detail extraction" region
|
|
/// below) supplies retail Detail_HelpType text for 42 of the catalog's OWN
|
|
/// dispatched leaf verbs too, which <c>ChatCommandRouter</c> now prefers
|
|
/// over the catalog's acdream-authored summaries. This table is the single
|
|
/// <c>/help <verb></c> retail-text source for BOTH catalog-dispatched
|
|
/// and non-catalog verbs; <see cref="RetailClientCommandCatalog.TryGetHelpText"/>
|
|
/// is now purely the fallback for the catalog verbs not yet extracted.
|
|
///
|
|
/// <para>
|
|
/// The named constants above <see cref="ByVerb"/> (<see cref="Tell"/>,
|
|
/// <see cref="Reply"/>, <see cref="Retell"/>, <see cref="MonarchReply"/>,
|
|
/// <see cref="PatronReply"/>, <see cref="Day"/>, <see cref="Log"/>,
|
|
/// <see cref="Render"/>, <see cref="Motd"/>, <see cref="AllegianceOverview"/>,
|
|
/// <see cref="HouseOverview"/>) are verbatim retail text recovered from
|
|
/// <c>docs/research/named-retail/acclient_2013_pseudo_c.txt</c> by the
|
|
/// recipe in the command-registry doc §5 (scan each <c>Help*</c>
|
|
/// function's byte extent for <c>push imm32</c> into <c>.rdata</c>). Entries
|
|
/// that could not be recovered this way (@render's option list) are NOT
|
|
/// fabricated — they are simply absent from this table; the lookup falls
|
|
/// through to a generic "no detailed help" line rather than guess.
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// <b>Corrected 2026-08-09 at the CH4 REJECT-review, SHOULD-FIX 7:</b> the
|
|
/// paragraph above previously claimed "every entry" was verbatim retail
|
|
/// text, which was FALSE and directly contradicted <see cref="ByVerb"/>'s
|
|
/// own inline comment a few dozen lines below it. The ~35 CHANNEL
|
|
/// one-liners in <see cref="ByVerb"/> ("Sends text to your Fellowship
|
|
/// channel.", etc.) are acdream-authored SUMMARIES, not individually
|
|
/// hand-extracted retail strings — retail's own per-channel help text was
|
|
/// not recovered this slice.
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// <b>Campaign CH user-gate round 2, item 3 (2026-08-09):</b> the user
|
|
/// caught that <c>/help death</c> printed an acdream META-MESSAGE ("This is
|
|
/// a retail help-topic group; acdream has not yet extracted its exact
|
|
/// retail listing text…") instead of retail's real listing — this was the
|
|
/// class-doc overclaim's SHARP end. All 7 <c>HelpXxxGroup</c> nodes were
|
|
/// resolved that round: <see cref="DeathGroupDetail"/>,
|
|
/// <see cref="StatusGroupDetail"/>, <see cref="TextGroupDetail"/>, and
|
|
/// <see cref="AllegiancesGroupDetail"/> were COMPLETE verbatim listings;
|
|
/// <see cref="ChannelsGroupDetail"/>, <see cref="ChattingGroupDetail"/>, and
|
|
/// <see cref="CommandsGroupDetail"/> were left PARTIAL with an UNVERIFIED
|
|
/// note. 7 of the ~35 channel one-liners (a/guild/gu, general/cg, trade/ct,
|
|
/// lfg/clfg, roleplay/crp, society/soc, olthoi/o) were verbatim too; the
|
|
/// rest (fellowship/monarch/patron/vassals/covassal family, as standalone
|
|
/// <c>/help f</c>-style lookups) still route through the unresolved
|
|
/// mechanism below and remain acdream summaries — that part of the gap is
|
|
/// unchanged this round; see <c>ByVerb</c> and
|
|
/// <c>RetailCommandHelpTableTests.ChannelVerb_StillAcdreamSummary_UnresolvedChannelHackFamily</c>
|
|
/// for the record of exactly which verbs. (Those verbs' OWN standalone help
|
|
/// registration was never confirmed independently of the group-listing
|
|
/// mechanism decoded below, so they are left untouched rather than
|
|
/// speculatively upgraded.)
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// <b>Round 4 (2026-08-10): the 3 PARTIAL groups are now COMPLETE, and the
|
|
/// meta-notice text itself is GONE from every user-visible string.</b> The
|
|
/// blocker was never really <c>HelpStupidChannelHack @0x0056f290</c>'s own
|
|
/// code — the fragments Binary Ninja renders as vtable-slot dereferences
|
|
/// (<c>&ClientCommunicationSystem::`vftable'.RecvNotice_StartBarberNotice</c>
|
|
/// etc.) are the SAME pooled/mislabeled-data artifact this campaign has hit
|
|
/// before (AP-186's own precedent): they are DATA pointers into
|
|
/// <c>.rdata</c>, not real vtable dispatch, and dereferencing their actual
|
|
/// operand addresses (found by reading <c>HelpStupidChannelHack</c>'s own
|
|
/// disassembly for the <c>push imm32</c> immediately preceding each
|
|
/// constructor call, since BN's line-grouped rendering hides the true
|
|
/// instruction order) resolves cleanly: the function builds
|
|
/// <c>"@" + tag + " - Sends a broadcast to your " + ChannelName + ".\n"</c>
|
|
/// where <c>tag</c> is a single character sliced out of a shared wide
|
|
/// literal <c>U"fvpca"</c> (reading a WIDE string through a NARROW
|
|
/// <c>char*</c> naturally truncates to one character at the first zero high
|
|
/// byte — the "hack" the function's own retail name calls out) and
|
|
/// <c>ChannelName</c> comes from <c>ChannelSystem::GetChannelName</c>'s own
|
|
/// literal switch table (also swept directly, not guessed:
|
|
/// <c>data_0x7d0478</c>="Allegiance", plus "Co-vassals"/"Monarch"/"Patron"/
|
|
/// "Vassals"/"Fellowship" already visible in that function's straight-line
|
|
/// decompiled body). See <see cref="AllegianceBroadcastLine"/> through
|
|
/// <see cref="FellowshipBroadcastLine"/> for the six resolved lines and
|
|
/// their per-line address citations.
|
|
///
|
|
/// <para>
|
|
/// With that decoded, <see cref="ChannelsGroupDetail"/> (which is ENTIRELY
|
|
/// six <c>HelpStupidChannelHack</c> calls, confirmed by reading
|
|
/// <c>HelpChannelsGroup</c>'s Detail branch directly) is now a COMPLETE
|
|
/// verbatim listing. <see cref="ChattingGroupDetail"/> needed one more
|
|
/// piece: its "@reply" entry does not call a per-tag summary — it calls
|
|
/// <c>ClientCommunicationSystem::HelpReply @0x00577A50</c> with
|
|
/// <c>Summary_HelpType</c>, and that function (read directly, not assumed)
|
|
/// unconditionally concatenates THREE lines regardless of which tag it was
|
|
/// passed (reply, then pr, then mr) — a genuine retail quirk, ported
|
|
/// faithfully per CLAUDE.md's "do not fix the decompiled code" rule; see
|
|
/// <see cref="ReplySummaryLine"/>/<see cref="PatronReplySummaryLine"/>/
|
|
/// <see cref="MonarchReplySummaryLine"/>. <see cref="CommandsGroupDetail"/>
|
|
/// (retail's <c>HelpAllGroup</c>, the "lists all commands" topic) is a
|
|
/// straight-line concatenation of EVERY other group's Detail branch plus a
|
|
/// handful of its own short one-liners (all swept directly from
|
|
/// <c>0x0057E7F0</c>-<c>0x0057EBA0</c>) — including a CONFIRMED retail
|
|
/// duplicate (the saveui/loadui short-summary pair appears twice with no
|
|
/// conditional between the two occurrences, ported as-is) and one line
|
|
/// (<c>@say</c>) whose own retail literal has NO trailing newline, unlike
|
|
/// every sibling line — also ported as-is; retail's own output runs
|
|
/// <c>@say</c> straight into <c>@tell</c> with no line break. Zero
|
|
/// remaining UNVERIFIED notes in any of the three former PARTIAL groups.
|
|
/// Closes ISSUES.md #364.
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// <b>The meta-marker sweep.</b> Every honesty marker that had leaked into
|
|
/// USER-VISIBLE text — the "[IMPLEMENTED]"/"[IMPLEMENTED, also @x]" tags
|
|
/// littered through <see cref="AllegianceOverview"/>/<see cref="HouseOverview"/>,
|
|
/// the "NOT YET IMPLEMENTED in acdream" sentences appended to
|
|
/// <see cref="Day"/>/<see cref="Log"/>/<see cref="Render"/>/<see cref="Motd"/>,
|
|
/// and the "Subcommands NOT marked [IMPLEMENTED]…" trailer on both overview
|
|
/// blocks — is now GONE from the strings themselves. Per CLAUDE.md, honesty
|
|
/// markers belong in code comments and the divergence register, never in
|
|
/// text a player can read. Every one of those consts is now checked against
|
|
/// the raw retail data dump the pseudo-C file itself carries for long
|
|
/// consolidated string tables (e.g. <c>char const data_7dfb58[0x879] =
|
|
/// "@allegiance boot …</c>) — those blocks are ALREADY fully decoded by
|
|
/// Binary Ninja with no BN artifact to work around, so they were read
|
|
/// directly rather than re-swept, and confirmed the "[IMPLEMENTED]" tags
|
|
/// were 100% acdream additions with no retail counterpart (not even a
|
|
/// stray character). <see cref="Log"/> and <see cref="Motd"/> also picked
|
|
/// up MORE retail text than they had before while their meta tails were
|
|
/// being removed — the prior extractions had silently dropped extra
|
|
/// sentences/paragraphs present in the same data block (<c>data_7df7f8</c>
|
|
/// and <c>data_7da300</c> respectively); <see cref="Render"/>'s previous
|
|
/// text was entirely acdream-authored ("Forwards to the client's
|
|
/// render-option surface…") and is replaced with the REAL retail usage
|
|
/// string (<c>render_option_usage</c> @ <c>0x0079B878</c>, found by
|
|
/// grepping the pseudo-C for a literal <c>@render</c> string instead of
|
|
/// trusting the earlier "not decodable" note).
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// <b><c>messagetypes</c> is now a real construction, not an acdream
|
|
/// summary.</b> Retail's <c>HelpMessageTypes @0x0056E5D0</c> really does
|
|
/// build its text at runtime — but by walking a FIXED whitelist
|
|
/// (<c>LogTextTypeEnumMapper::IsLegalChannel @0x006AFF40</c>, 14 ids) and a
|
|
/// FIXED per-id name table (<c>LogTextTypeEnumMapper::LogTextTypeToString
|
|
/// @0x006AFF90</c>, a literal switch), joined with <c>", "</c>
|
|
/// (<c>data_0x794334</c>) and wrapped in <c>"Squelch channels are as
|
|
/// follows:\n %s\n"</c> (<c>gmCCommunicationSystem::GetListofSquelchChannels
|
|
/// @0x00589EB0</c> /
|
|
/// <c>gmCCommunicationSystem::ConvertSQToPString @0x00589A80</c>'s
|
|
/// <c>arg3!=0</c> "enumerate every legal channel" branch — the
|
|
/// <c>SquelchInfo</c> it iterates is a SYNTHETIC all-squelched object built
|
|
/// solely to walk every legal id, not the player's actual squelch state).
|
|
/// Since the whitelist and the name table are both fixed, the output is
|
|
/// itself a compile-time constant — but <see cref="MessageTypesDetail"/>
|
|
/// PORTS the construction (the ordered id/name table +
|
|
/// <see cref="BuildMessageTypesDetail"/>'s exact join/prefix logic) rather
|
|
/// than hand-authoring the already-known joined string, so a future
|
|
/// whitelist change is a one-line table edit instead of a re-derivation.
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// <b>Campaign CH user-gate round 3 (2026-08-10), findings (b)/(c):</b> the
|
|
/// round 2 pass extracted the individual STRINGS byte-exact but never
|
|
/// traced <c>ClientCommunicationSystem::DoHelp @0x0057f9e0</c>'s complete
|
|
/// PRINT SEQUENCE — so the CONTENT was right while the SHAPE (how many
|
|
/// transcript entries, in what order, with what wrapping) was still
|
|
/// acdream-invented. Traced this round via a byte-sweep of DoHelp's own
|
|
/// range (0x57f9e0-0x57fe7e) plus every Summary-branch it calls into
|
|
/// (HelpEmote/HelpSquelch/HelpStatusGroup/HelpTextGroup/HelpAllGroup),
|
|
/// against the PDB-paired <c>C:\Users\erikn\Downloads\acclient.exe</c>
|
|
/// (verified MATCH). DoHelp ALWAYS prints via exactly two
|
|
/// <c>AddTextToScroll</c> calls (never one concatenated blob), both typed
|
|
/// <c>0</c> (informational):
|
|
/// <list type="number">
|
|
/// <item>the SAME <see cref="HelpPrefixNote"/> line, unconditionally —
|
|
/// for the bare listing AND for every successful <c>/help <verb></c>
|
|
/// lookup;</item>
|
|
/// <item>bare <c>/help</c>: <see cref="AvailableHelpListing"/> (13 items,
|
|
/// straight-line concatenation of 8 inline literals plus 5 delegated to
|
|
/// each group's own Summary_HelpType branch, in DoHelp's exact source
|
|
/// order). <c>/help <verb></c> when the verb resolves:
|
|
/// <see cref="ForMoreInformationPrefix"/> immediately followed by the
|
|
/// verb's own Detail-branch text (e.g. <see cref="DeathGroupDetail"/>) —
|
|
/// ONE string, no blank line between the prefix and the listing, because
|
|
/// retail's own handler call APPENDS into the same accumulator the
|
|
/// prefix was built into (<c>ClientCommunicationSystem::DoHelp</c>,
|
|
/// arg2>0 branch, the <c>eax_35(2, var_18, &var_10)</c> call).</item>
|
|
/// </list>
|
|
/// When the verb does NOT resolve, DoHelp prints ONE entry,
|
|
/// <see cref="UnknownCommand"/>, typed <c>0x1A</c> (<c>ClientLocal</c>) —
|
|
/// retail routes that type to the SpewBox exclusively, never the chat
|
|
/// window (<c>docs/research/2026-08-09-chat-retail-interface-text.md</c>
|
|
/// §2.1/§2.2).
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// <b>Issue #363 (2026-08-10):</b> <c>ChatCommandRouter</c> now routes this
|
|
/// fallback (and every other <c>0x1A</c> command-refusal call site) through
|
|
/// <c>IChatCommandFeedback.ShowInterfaceText</c> — an optional hook the host
|
|
/// wires to <c>RuntimeCommunicationState.AddText</c>, the same SpewBox
|
|
/// chokepoint every other producer of interface text uses. The retained
|
|
/// <c>ChatVM</c> implements this four-member feedback seam without entering
|
|
/// command-routing code. Closes ISSUES.md #367 and retires register row
|
|
/// AP-186.
|
|
/// </para>
|
|
/// </summary>
|
|
public static class RetailCommandHelpTable
|
|
{
|
|
// acclient_2013_pseudo_c.txt:1030771 (data_7dae40).
|
|
public const string Tell =
|
|
"@tell <name>, <text> - Sends a long-distance, private message to the specified character. Note that you must put a comma after the character's name.";
|
|
|
|
// acclient_2013_pseudo_c.txt:1030742/1030745.
|
|
public const string Reply =
|
|
"@reply <text> - Sends the text to the last person who @tell'd you. You may also use @r or @rp.";
|
|
|
|
// acclient_2013_pseudo_c.txt:1030753/1030757.
|
|
public const string Retell =
|
|
"@retell <text> - Sends the text to the last person you @tell'd. You may also use @rt.";
|
|
|
|
// acclient_2013_pseudo_c.txt:394980 (data_0x7e11e0). RE-SWEPT byte-exact
|
|
// Campaign CH user-gate round 3 (2026-08-10) via sweep_weenie_strings.py
|
|
// --range 0x57f9e0 0x57fe7e --ascii-only against the PDB-paired
|
|
// C:\Users\erikn\Downloads\acclient.exe (verified MATCH). The ORIGINAL
|
|
// extraction dropped the leading blank line and the trailing double
|
|
// newline that are part of retail's own literal — this is DoHelp's
|
|
// OWN, always-first scroll entry (a dedicated AddTextToScroll call,
|
|
// never concatenated with what follows), printed unconditionally both
|
|
// for the bare /help listing and for every successful /help <verb>
|
|
// lookup. See the class remarks for the complete print-sequence trace.
|
|
public const string HelpPrefixNote =
|
|
"\nNote: You may substitute a forward slash (/) for the at symbol (@).\n\n";
|
|
|
|
// acclient_2013_pseudo_c.txt:395087 (data_0x7e1178), swept alongside
|
|
// HelpPrefixNote above. DoHelp's arg2>0 (verb-specified) branch builds
|
|
// this as the START of its second scroll entry, then the resolved
|
|
// verb's own Detail-branch handler APPENDS its listing directly onto
|
|
// the SAME accumulator (ClientCommunicationSystem::DoHelp, the
|
|
// `eax_35(2, var_18, &var_10)` call) -- so the retail-faithful port is
|
|
// string concatenation with NO separator, not two entries. The literal
|
|
// text "<command>" is NOT a format placeholder -- the decomp shows a
|
|
// straight PStringBase construction with no sprintf/substitution call
|
|
// between this literal and its use, so retail never actually inserts
|
|
// the real verb name here. Ported verbatim per CLAUDE.md's "do not fix
|
|
// the decompiled code" rule, even though it reads like an unfinished
|
|
// dev message.
|
|
public const string ForMoreInformationPrefix =
|
|
"For more information, type @help <command>.\n";
|
|
|
|
// acclient_2013_pseudo_c.txt:395052 (u"Unknown command", UTF-16LE) --
|
|
// DoHelp's fallback when the verb hash lookup fails, or resolves to an
|
|
// entry with no registered help callback. Retail types this 0x1A
|
|
// (ClientLocal) -- SpewBox-only; see the class remarks' routing note --
|
|
// ChatCommandRouter routes it through IChatCommandFeedback.ShowInterfaceText
|
|
// (issue #363), closing #367.
|
|
public const string UnknownCommand = "Unknown command";
|
|
|
|
// @mr/@pr are registered with a NULL function pointer in the 2013
|
|
// build (verified at 0x00583041/0x005830C1 — arg3 is 0), so they never
|
|
// execute locally in retail OR acdream — typing one sends the literal
|
|
// text to the server. CH4 REJECT-review SHOULD-FIX 7 (2026-08-09):
|
|
// the strings below were previously FABRICATED acdream summaries; the
|
|
// real retail-registered help function (HelpReply @0x00577A50) is
|
|
// shared across @reply/@r/@rp/@mr/@pr and IS the source of a per-verb
|
|
// detail line for each, extracted verbatim below —
|
|
// acclient_2013_pseudo_c.txt:1030734 (data_7daa08) for @mr,
|
|
// acclient_2013_pseudo_c.txt:1030738 (data_7daa80) for @pr. Retail's
|
|
// own strings have a double space before "you" — confirmed byte-level,
|
|
// not a typo. (HelpReply's full concatenation across all 5 shared
|
|
// verbs is more involved than a single-string extraction can safely
|
|
// confirm from the pseudo-C alone — a BN decomp string-temporary
|
|
// pattern reuses the output-parameter stack slot, which risks a
|
|
// misread; only the two per-verb detail lines requested by the review
|
|
// are pinned here, not a full re-derivation of HelpReply's output.)
|
|
public const string MonarchReply =
|
|
"@mr <text> - Sends the text to the last person who used @m to send you a message. This only works for monarchs.";
|
|
|
|
public const string PatronReply =
|
|
"@pr <text> - Sends the text to the last vassal who used @p to send you a message.";
|
|
|
|
// acclient_2013_pseudo_c.txt:1031093 (char const data_7de280[0x77] =
|
|
// "@day - A…\n", 0) -- BN already fully decodes this consolidated data
|
|
// block, no vtable-slot artifact to work around; read directly rather
|
|
// than re-swept. Round 4 (2026-08-10): dropped the acdream
|
|
// "NOT YET IMPLEMENTED in acdream" tail -- honesty markers belong in
|
|
// comments/the register, never in text a player can read (CLAUDE.md).
|
|
// acdream's client-side "@day" implementation status, if any, is
|
|
// tracked in the divergence register, not here. Shared with
|
|
// StatusGroupDetail's own "@day" line -- same data block, same text.
|
|
public const string DayLine =
|
|
"@day - A toggle that lightens the outdoor landscape. Note that this command may take several seconds to take effect. \n";
|
|
|
|
public const string Day = DayLine;
|
|
|
|
// acclient_2013_pseudo_c.txt:1031192-1031198 (char const
|
|
// data_7df7f8[0x2ca] = "@log <name> - …\n" "@log AClog.txt - …\n" "@log
|
|
// - …\n", 0) -- read directly off the pseudo-C's own consolidated data
|
|
// dump. Round 4 (2026-08-10): the prior extraction had silently
|
|
// dropped the middle sentence ("Choose the file you are copying to by
|
|
// naming it in the command.") and BOTH trailing paragraphs (the
|
|
// AClog.txt example and the "stop the process" line) in addition to
|
|
// carrying the "NOT YET IMPLEMENTED" meta tail -- all three retail
|
|
// sentences are restored here, verbatim, with no acdream commentary.
|
|
public const string Log =
|
|
"@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";
|
|
|
|
// Round 4 (2026-08-10): the prior text here ("Forwards to the client's
|
|
// render-option surface (retail: SmartBox::HandleRenderOption). NOT YET
|
|
// IMPLEMENTED in acdream…") was entirely acdream-authored, not retail
|
|
// text -- the class remarks previously (incorrectly) called @render's
|
|
// option list "not decodable". A direct grep for a literal "@render"
|
|
// string in the pseudo-C finds it: `char const render_option_usage[0x98]
|
|
// @ 0x0079B878 = "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"` -- the real retail usage text
|
|
// shown by SmartBox::HandleRenderOption for bad/missing arguments
|
|
// (acclient_2013_pseudo_c.txt:94759/94767), swept verbatim, no
|
|
// paraphrase.
|
|
public const string Render =
|
|
"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";
|
|
|
|
// acclient_2013_pseudo_c.txt:1030670-1030676 (char const
|
|
// data_7da300[0xdb] = "@allegiance motd - …\n" "@allegiance motd set
|
|
// …\n" "@allegiance motd clear- …\n", 0) -- read directly off the
|
|
// pseudo-C's own consolidated data dump. Round 4 (2026-08-10): the
|
|
// prior extraction paraphrased this into a single acdream sentence and
|
|
// appended a "NOT YET IMPLEMENTED in acdream (TS-68)" meta tail; restored
|
|
// to retail's real 3-line shape, including the "clear-" (no space
|
|
// before the dash) typo confirmed present in the raw data -- ported as
|
|
// found per CLAUDE.md, not "fixed".
|
|
public const string Motd =
|
|
"@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";
|
|
|
|
// Split out of AllegianceOverview's own data block (data_0x7dfb58) at
|
|
// the consolidated review round (2026-08-10) so the const declaration
|
|
// order matches use; see the class remarks on AllegianceOverview below.
|
|
public const string AllegianceWarningLine =
|
|
" WARNING! Officers banning or booting a character by account could wind up in a situation where they are no longer in the allegiance if they boot a character that is above them in the hierarchy.\n";
|
|
|
|
// acclient_2013_pseudo_c.txt:1031234 (data_7e03d4) plus the full
|
|
// per-subcommand block at 1031210-1031230 (data_7dfb58). Consolidated
|
|
// review round (2026-08-10), SHOULD-FIX 1 byproduct: a fresh full-length
|
|
// byte sweep of HelpAllegiance @0x0057ae10 (0x57ae10-0x57aef0, no
|
|
// max_chars truncation this time) turned up the " WARNING! Officers
|
|
// banning..." line between "ban list" and "info" that the original
|
|
// extraction dropped — confirmed present in the SAME data block
|
|
// (data_0x7dfb58) this const already cites, not a new source. Restored
|
|
// verbatim, including the retail leading space.
|
|
//
|
|
// Round 4 (2026-08-10): every "[IMPLEMENTED]"/"[IMPLEMENTED, also
|
|
// @x]"/"[IMPLEMENTED as @ab]" tag AND the trailing "Subcommands NOT
|
|
// marked [IMPLEMENTED]…" sentence below were acdream additions with NO
|
|
// retail counterpart — confirmed by reading data_7dfb58's own raw
|
|
// consolidated dump (acclient_2013_pseudo_c.txt:1031210-1031230), which
|
|
// BN already fully decodes with no vtable-slot artifact to work around.
|
|
// The pristine dump's "broadcast" line ends "...Also: @ab\n" (no
|
|
// bracket) and the "hometown" line ends "...tied to one.\n" (no
|
|
// bracket, no alias mention at all) — both removed here. Implementation
|
|
// status never belongs in this verbatim user-visible string.
|
|
public const string AllegianceOverview =
|
|
"@allegiance - Commands to help manage your allegiance.\n"
|
|
+ "@allegiance boot [-account] <name> - Removes a character from your allegiance.\n"
|
|
+ "@allegiance ban <add/remove> <name> - Bans all characters on the given character's account from your allegiance (and boots them too!)\n"
|
|
+ "@allegiance ban list - List the characters whose accounts are banned from your allegiance.\n"
|
|
+ AllegianceWarningLine
|
|
+ "@allegiance info <name> - Requests information on a member of your allegiance.\n"
|
|
+ "@allegiance chat <on/off> - Turn allegiance chat on and off.\n"
|
|
+ "@allegiance chat kick <name>[, <reason>] - Kick a player temporarily from the allegiance chat room.\n"
|
|
+ "@allegiance chat gag <name> - Gags a player so that they cannot see or speak in the allegiance chat room for 5 minutes.\n"
|
|
+ "@allegiance chat ungag <name> - Ungags a gagged allegiance member so that they may once again see and speak in the allegiance chat room.\n"
|
|
+ "@allegiance broadcast <message> - Broadcast a message to the entire allegiance. Limited to 10/day. Also: @ab\n"
|
|
+ "@allegiance officer <add/set> <level #> <name> - Assigns the position of officer, with the given level of permissions, to the named character.\n"
|
|
+ "@allegiance officer <remove> <name> - Removed the named character as an allegiance officer.\n"
|
|
+ "@allegiance officer clear - Clears all officer positions.\n"
|
|
+ "@allegiance officer [list] - list your allegiance officer. Can be used by anyone in an allegiance.\n"
|
|
+ "@allegiance title set <level #> <title> - Sets the title of the given officer level.\n"
|
|
+ "@allegiance title clear - Clears all officer titles.\n"
|
|
+ "@allegiance title [list] - Lists all the officer titles for your allegiance.\n"
|
|
+ "@allegiance name <set/clear> - Displays, sets, or clears the name of your allegiance.\n"
|
|
+ "@allegiance lock <on/off/toggle/check> - Locks, unlocks, or displays the locked state of your allegiance.\n"
|
|
+ "@allegiance lock bypass <clear/name> - Sets, clears, or displays a single character as an approved vassal. That character may then swear into a locked allegiance.\n"
|
|
+ "@allegiance hometown - Recalls you to your allegiance bindstone, if your allegiance has tied to one.\n"
|
|
+ "@allegiance motd - Displays or sets the message of the day for your allegiance.\n";
|
|
|
|
// acclient_2013_pseudo_c.txt:1031041 (char const data_7dd908[0x5c] =
|
|
// "@house - Commands that help you manage your house, including guest
|
|
// and storage management.\n", 0) -- the group-intro one-liner, shared
|
|
// verbatim below with CommandsGroupDetail's own "@house" fragment (same
|
|
// underlying retail string, per HelpAllGroup's own direct call to
|
|
// PStringBase::PStringBase with this exact literal).
|
|
public const string HouseOneLiner =
|
|
"@house - Commands that help you manage your house, including guest and storage management.\n";
|
|
|
|
// acclient_2013_pseudo_c.txt:1031017-1031037 (char const
|
|
// data_7dd3d0[0x534] = "@house abandon - …\n" … "@house available - See
|
|
// @hslist\n", 0) -- read directly off the pseudo-C's own consolidated
|
|
// data dump; BN already fully decodes this block with no vtable-slot
|
|
// artifact to work around. Round 4 (2026-08-10): every
|
|
// "[IMPLEMENTED]"/"[IMPLEMENTED, also @x]"/"[IMPLEMENTED, alias of
|
|
// mansion_recall]" tag AND the trailing "Subcommands NOT marked
|
|
// [IMPLEMENTED]…" sentence below were acdream additions with no retail
|
|
// counterpart. The "@house available" line's retail literal is exactly
|
|
// "@house available - See @hslist\n" — no trailing period, and none of
|
|
// the bracketed "[see @hslist, IMPLEMENTED]" text the old version
|
|
// appended. Implementation status never belongs in this verbatim
|
|
// user-visible string.
|
|
public const string HouseOverview =
|
|
HouseOneLiner
|
|
+ "@house abandon - Abandons your house.\n"
|
|
+ "@house boot <name> - Removes a player from your house.\n"
|
|
+ "@house boot -all - Removes everyone from your house.\n"
|
|
+ "@house guest add <name> - Adds players to your house guest list.\n"
|
|
+ "@house guest remove <name> - Removes players from your house guest list.\n"
|
|
+ "@house guest add_allegiance - Adds your allegiance to the guest list.\n"
|
|
+ "@house guest remove_allegiance - Removes your allegiance from the guest list.\n"
|
|
+ "@house guest remove_all - Removes all guests from your house guest list.\n"
|
|
+ "@house guest list - Shows the current guest list.\n"
|
|
+ "@house recall - Teleports you to your house.\n"
|
|
+ "@house storage add <name> - Gives a player permission to use your house storage.\n"
|
|
+ "@house storage remove <name> - Removes permission to use your house storage from a player.\n"
|
|
+ "@house storage add_allegiance - Grants storage permission to your allegiance.\n"
|
|
+ "@house storage remove_allegiance - Removes storage permission from your allegiance.\n"
|
|
+ "@house storage remove_all - Removes all storage permissions from guests.\n"
|
|
+ "@house open - Creates an open house.\n"
|
|
+ "@house close - Closes your house.\n"
|
|
+ "@house hooks on|off - Makes the hooks in your house visible or invisible.\n"
|
|
+ "@house mansion_recall - Teleports you to your allegiance mansion or villa.\n"
|
|
+ "@house alleg_recall - Teleports you to your allegiance mansion or villa.\n"
|
|
+ "@house available - See @hslist\n";
|
|
|
|
// The 7 retail-registered "group index" nodes — retail registers them
|
|
// with a NULL func (like @mr/@pr); typing one bare reaches the server.
|
|
// Only @help <group> shows anything.
|
|
//
|
|
// Campaign CH user-gate round 2, item 3 (2026-08-09): the user caught
|
|
// that "/help death" printed an acdream META-MESSAGE instead of
|
|
// retail's real listing. Extracted verbatim via
|
|
// tools/pdb-extract/sweep_weenie_strings.py (generalized this round for
|
|
// narrow PStringBase<char> literals — the Help* family uses 8-bit ASCII,
|
|
// not the UTF-16LE ECM_UI notice shape the script originally targeted)
|
|
// against the PDB-paired C:\Users\erikn\Downloads\acclient.exe. Method:
|
|
// each HelpXxxGroup function (ClientCommunicationSystem::HelpXxxGroup,
|
|
// decomp §5) is a single `if (arg2 != Summary_HelpType) { <DETAIL:
|
|
// straight-line concatenation of subcommand summaries> } else { <the
|
|
// group's own one-line summary> }`; the exact byte extent (this
|
|
// function's start VA to the next function's start VA) was read from
|
|
// the pseudo-C's own function-header addresses, and every extent was
|
|
// swept with --ascii-only. Four groups decoded COMPLETELY that round
|
|
// (every subcommand summary is either an inline literal in the group
|
|
// function itself or a direct `HelpXxx(this, Summary_HelpType, "@tag",
|
|
// arg4)` call whose OWN summary text was independently swept from
|
|
// HelpXxx's own extent): death, status, text, allegiances.
|
|
//
|
|
// Round 4 (2026-08-10): the remaining three (commands, channels,
|
|
// chatting) are ALSO now COMPLETE — see the class remarks' "3 PARTIAL
|
|
// groups are now COMPLETE" paragraph for the full HelpStupidChannelHack
|
|
// decode. Nothing in this file emits an UNVERIFIED note for any of the
|
|
// 7 groups any more.
|
|
private const string ChannelsGroupSummary =
|
|
"@help channels - How to communicate with people in your allegiance or fellowship.";
|
|
|
|
// acclient_2013_pseudo_c.txt:391002 (0x0057b1b0). HelpChattingGroup's
|
|
// own one-liner — not used standalone below (its Detail branch does
|
|
// NOT prepend it, matching every other group), kept for documentation
|
|
// parity with the other two Summary consts above/below.
|
|
private const string ChattingGroupSummaryVerbatim =
|
|
"@help chatting - How to chat publically and privately.";
|
|
|
|
private const string CommandsGroupSummary =
|
|
"@help commands - Lists all commands.";
|
|
|
|
// acclient_2013_pseudo_c.txt:391365-391436 (HelpDeathGroup, decomp
|
|
// §5 sweep, addresses 0x57b913-0x57ba56). The Detail branch is a
|
|
// straight-line concatenation with NO leading group-summary line (that
|
|
// string is exclusive to the Summary_HelpType branch, i.e. what a
|
|
// PARENT listing shows about "death" as one entry — @help death itself
|
|
// never shows it).
|
|
public const string DeathGroupDetail =
|
|
"@permit - Commands to give or revoke permission for others to loot your corpse.\n"
|
|
+ "@consent - Commands to help you manage the corpse-looting permissions that others give you.\n"
|
|
+ "@corpse - Displays the location of your last outdoor death.\n"
|
|
+ "@die - Kills your character and leaves a corpse, returning you to your lifestone.\n"
|
|
+ "@lifestone - Returns you to the last lifestone you used without killing you.\n"
|
|
+ "@marketplace - Teleports you to the Marketplace of Dereth.\n"
|
|
+ "@pkarena - Teleports you to the PK Arena. You must be PK to use this command.\n"
|
|
+ "@pklarena - Teleports you to the PKL Arena. You must be PKL to use this command.\n";
|
|
|
|
// acclient_2013_pseudo_c.txt:392030-392093 (HelpStatusGroup). Same
|
|
// shape as HelpDeathGroup — Detail branch, no leading group-summary
|
|
// line. The trailing space before "\n" on the @day line is retail's
|
|
// own byte content (data_0x7de280), not an acdream typo.
|
|
public const string StatusGroupDetail =
|
|
"@age - Displays your total gameplay time.\n"
|
|
+ "@birth - Displays when your character was created.\n"
|
|
+ DayLine
|
|
+ "@endurance - Explains how endurance affects your character.\n"
|
|
+ "@framerate - Toggles the framerate display.\n"
|
|
+ "@loc - Displays your current position.\n"
|
|
+ "@pklite - Sets your status to Player Killer Lite. Type @help pklite for more details.\n"
|
|
+ "@version - Tells you what version of the software you are using.\n";
|
|
|
|
// acclient_2013_pseudo_c.txt:392209-392279 (HelpTextGroup).
|
|
public const string TextGroupDetail =
|
|
"@clear - Clears the chat box of all text.\n"
|
|
+ "@filter - Commands to filter out incoming messages.\n"
|
|
+ "@unfilter - Commands to remove filters from incoming messages.\n"
|
|
+ "@loadfile - Reads in the given text file and executes each line in the chat entry field.\n"
|
|
+ "@log - Commands to echo chat text to a logfile.\n"
|
|
+ "@title <new title> - Sets the title of the popup chat window.\n";
|
|
|
|
// acclient_2013_pseudo_c.txt:393033-393059 (HelpAllegiancesGroup) —
|
|
// the shortest group, only 2 lines.
|
|
public const string AllegiancesGroupDetail =
|
|
"@allegiance - Commands to help manage your allegiance.\n"
|
|
+ "@allegiance motd - Displays or sets the message of the day for your allegiance, see @help motd for more information.\n";
|
|
|
|
// ── HelpStupidChannelHack, decoded (round 4, 2026-08-10) ────────────
|
|
//
|
|
// ClientCommunicationSystem::HelpStupidChannelHack @0x0056f290 builds
|
|
// one line as `"@" + tag + " - Sends a broadcast to your " +
|
|
// ChannelName + ".\n"`. Read directly off the function's own
|
|
// disassembly (the `push imm32` immediately preceding each
|
|
// PStringBase::PStringBase constructor call, since BN's line-grouped
|
|
// pseudo-C hides the real instruction order and mislabels each pushed
|
|
// address as an unrelated vtable slot):
|
|
// - "@" @ data_0x7d0424, " - Sends a broadcast to your " @
|
|
// data_0x7d0428, trailing ".\n" @ data_0x7d0448 -- all three swept
|
|
// via tools/pdb-extract/sweep_weenie_strings.py --anchor against
|
|
// the PDB-paired C:\Users\erikn\Downloads\acclient.exe.
|
|
// - `tag` is one character sliced out of a shared wide literal
|
|
// `U"fvpca"` (plus one more, `U"mh,."`, for 'm') -- reading a WIDE
|
|
// string through a NARROW char* naturally truncates to one
|
|
// character at the first zero high byte, which is the "hack" the
|
|
// function's own retail name calls out.
|
|
// - `ChannelName` comes from `ChannelSystem::GetChannelID(tag)` (read
|
|
// directly: 'a'=0x2000000, 'c'=0x1000000, 'm'=0x4000, 'p'=0x2000,
|
|
// 'v'=0x1000, 'f'=0x800) then `ChannelSystem::GetChannelName(id)`'s
|
|
// own literal switch table (also read directly, not guessed):
|
|
// 0x2000="Patron", 0x4000="Monarch", 0x800="Fellowship",
|
|
// 0x1000="Vassals", 0x1000000="Co-vassals", and 0x2000000 resolves
|
|
// through one more mislabeled vtable slot that sweeps to
|
|
// data_0x7d0478="Allegiance".
|
|
public const string AllegianceBroadcastLine = "@a - Sends a broadcast to your Allegiance.\n";
|
|
public const string CovassalBroadcastLine = "@c - Sends a broadcast to your Co-vassals.\n";
|
|
public const string MonarchBroadcastLine = "@m - Sends a broadcast to your Monarch.\n";
|
|
public const string PatronBroadcastLine = "@p - Sends a broadcast to your Patron.\n";
|
|
public const string VassalBroadcastLine = "@v - Sends a broadcast to your Vassals.\n";
|
|
public const string FellowshipBroadcastLine = "@f - Sends a broadcast to your Fellowship.\n";
|
|
|
|
// HelpChannelsGroup's Detail branch (acclient_2013_pseudo_c.txt:387166-
|
|
// 387194) is ENTIRELY 6 HelpStupidChannelHack calls, read in this exact
|
|
// call order (a, c, m, p, v, f) -- no leading group-summary line (that
|
|
// string is exclusive to the Summary_HelpType branch). COMPLETE
|
|
// verbatim listing as of round 4; no UNVERIFIED remainder.
|
|
public const string ChannelsGroupDetail =
|
|
AllegianceBroadcastLine
|
|
+ CovassalBroadcastLine
|
|
+ MonarchBroadcastLine
|
|
+ PatronBroadcastLine
|
|
+ VassalBroadcastLine
|
|
+ FellowshipBroadcastLine;
|
|
|
|
// ClientCommunicationSystem::HelpReply @0x00577A50's Summary_HelpType
|
|
// branch (read directly, not assumed): it unconditionally concatenates
|
|
// THREE lines -- reply, then pr, then mr -- regardless of which tag it
|
|
// was called with. This is a genuine retail quirk (the shared handler
|
|
// ignores its own `arg3` tag parameter entirely in both branches),
|
|
// ported faithfully per CLAUDE.md's "do not fix the decompiled code"
|
|
// rule. Swept verbatim against the PDB-paired
|
|
// C:\Users\erikn\Downloads\acclient.exe, --range 0x577a50 0x577bd0.
|
|
public const string ReplySummaryLine = "@reply - Sends some text to the last person who @tell'd you.\n";
|
|
public const string PatronReplySummaryLine = "@pr - Sends some text to the last person who @p'd you.\n";
|
|
public const string MonarchReplySummaryLine = "@mr - Sends some text to the last person who @m'd you.\n";
|
|
|
|
// HelpChattingGroup's Detail branch (acclient_2013_pseudo_c.txt:390909-
|
|
// 390998) read in full, exact source order: chat, notell,
|
|
// HelpReply(Summary) [reply+pr+mr, see above], retell, say, tell, the
|
|
// SAME 6-line HelpStupidChannelHack block as ChannelsGroupDetail, afk.
|
|
// COMPLETE verbatim listing as of round 4. The "@say" line's own retail
|
|
// literal (data_0x7dadb4, swept) genuinely has NO trailing newline --
|
|
// confirmed by direct byte inspection, not a copy/paste drop -- so
|
|
// retail's own output runs "@say"'s line straight into "@tell"'s with
|
|
// no line break; ported as found.
|
|
public const string ChattingGroupDetail =
|
|
"@chat - Sets whether or not you receive normal chat.\n"
|
|
+ "@notell - Sets whether or not you receive @tell's.\n"
|
|
+ ReplySummaryLine
|
|
+ PatronReplySummaryLine
|
|
+ MonarchReplySummaryLine
|
|
+ "@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"
|
|
+ ChannelsGroupDetail
|
|
+ "@afk - Set your away-from-keyboard status.\n";
|
|
|
|
// Short one-liners HelpAllGroup builds itself (distinct retail literals
|
|
// from the catalog's own longer Detail text for the same verbs --
|
|
// e.g. this @saveui line is NOT SaveUiDetail's text; both are genuine
|
|
// retail strings, just from two different call sites). Swept verbatim,
|
|
// --range 0x57e7f0 0x57eba0 against the PDB-paired
|
|
// C:\Users\erikn\Downloads\acclient.exe.
|
|
public const string EmoteAndEmotesShortSummary =
|
|
"@emote - Performs a text emote.\n@emotes - Lists all standard emotes.\n";
|
|
public const string FillCompsShortSummary = "@fillcomps - Helps you buy components in bulk.\n";
|
|
public const string SaveUiShortSummary = "@saveui <filename> - Saves the current user interface.\n";
|
|
public const string LoadUiShortSummary = "@loadui <filename> - Loads a previously saved user interface.\n";
|
|
public const string FriendsShortSummary = "@friends - Helps you manage your friends list.\n";
|
|
public const string SquelchGroupShortSummary =
|
|
"@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";
|
|
|
|
// HelpAllGroup's Detail branch (acclient_2013_pseudo_c.txt:394006-394205,
|
|
// VA 0x0057E7FA-0x0057EB8E) read in full, exact source order:
|
|
// HelpAllegiancesGroup(Detail), the SAME 6-line HelpStupidChannelHack
|
|
// block, HelpChattingGroup(Detail), HelpDeathGroup(Detail), the emote
|
|
// one-liner pair, fillcomps, saveui(short), loadui(short), saveui(short)
|
|
// AGAIN, loadui(short) AGAIN, lockui, friends(short), the house
|
|
// one-liner, the squelch/unsquelch/messagetypes one-liner trio,
|
|
// HelpStatusGroup(Detail), HelpTextGroup(Detail). The saveui/loadui
|
|
// duplicate is a CONFIRMED retail duplicate -- the straight-line
|
|
// decompiled body repeats both calls with no conditional between the
|
|
// two occurrences (verified directly, not an acdream artifact) --
|
|
// ported as found. COMPLETE verbatim listing as of round 4; no
|
|
// UNVERIFIED remainder. Closes ISSUES.md #364.
|
|
public const string CommandsGroupDetail =
|
|
AllegiancesGroupDetail
|
|
+ ChannelsGroupDetail
|
|
+ ChattingGroupDetail
|
|
+ DeathGroupDetail
|
|
+ EmoteAndEmotesShortSummary
|
|
+ FillCompsShortSummary
|
|
+ SaveUiShortSummary
|
|
+ LoadUiShortSummary
|
|
+ SaveUiShortSummary
|
|
+ LoadUiShortSummary
|
|
+ LockUiDetail
|
|
+ FriendsShortSummary
|
|
+ HouseOneLiner
|
|
+ SquelchGroupShortSummary
|
|
+ StatusGroupDetail
|
|
+ TextGroupDetail;
|
|
|
|
// Campaign CH user-gate round 3 (2026-08-10), finding (b): DoHelp's
|
|
// bare-/help "else" branch (arg2<=0, acclient_2013_pseudo_c.txt:395089-
|
|
// 395268), swept whole against C:\Users\erikn\Downloads\acclient.exe
|
|
// (--range 0x57f9e0 0x57fe7e --ascii-only, verified MATCH). ONE
|
|
// straight-line concatenation of 13 items, in this exact source order:
|
|
// the "Available help:\n" header, then 8 items DoHelp builds from its
|
|
// OWN inline literals (allegiances/channels/chatting/death/fillcomps/
|
|
// friends/house — swept directly), interleaved with 5 items DoHelp
|
|
// builds by calling each group's OWN Summary_HelpType branch
|
|
// (HelpEmote/HelpSquelch/HelpStatusGroup/HelpTextGroup/HelpAllGroup —
|
|
// each independently swept from its own function range; every one of
|
|
// those 5 functions has the identical
|
|
// `if (arg2 != Summary_HelpType) {Detail} else {"@help X - ..."}`
|
|
// shape HelpEmote makes explicit at acclient_2013_pseudo_c.txt:388664).
|
|
// channels/chatting/commands' summary lines are reused from the
|
|
// consts above (independently cross-validated: both extractions agree
|
|
// byte-for-byte). Prints as DoHelp's SECOND scroll entry, right after
|
|
// HelpPrefixNote's own — see the class remarks and
|
|
// ChatCommandRouter's bare-/help handling.
|
|
public const string AvailableHelpListing =
|
|
"Available help:\n"
|
|
+ "@help allegiances - Commands to help you deal with your Allegiance.\n"
|
|
+ ChannelsGroupSummary + "\n"
|
|
+ ChattingGroupSummaryVerbatim + "\n"
|
|
+ "@help death - Commands for making, finding, and looting corpses.\n"
|
|
+ "@help emote - How to perform text and action emotes.\n"
|
|
+ "@help fillcomps - A command to help you buy components in bulk.\n"
|
|
+ "@help friends - Commands to help you manage your friends list.\n"
|
|
+ "@help house - Commands that help you manage your house, including guest and storage management.\n"
|
|
+ "@help squelch - Commands that let you block out messages from other players.\n"
|
|
+ "@help status - Commands that display useful information.\n"
|
|
+ "@help text - Commands that help you manage your text window.\n"
|
|
+ CommandsGroupSummary + "\n";
|
|
|
|
// ── Catalog leaf-verb Detail extraction (consolidated review round,
|
|
// 2026-08-10, SHOULD-FIX 1) ─────────────────────────────────────────
|
|
//
|
|
// RetailClientCommandCatalog's 47 top-level Definitions each carry an
|
|
// acdream-authored HelpText — a SUMMARY-shaped one-liner, not retail's
|
|
// Detail_HelpType(2) text. DoHelp @0x0057F9E0 calls a resolved verb's
|
|
// OWN registered help callback with Detail_HelpType for /help <verb>
|
|
// (the class remarks above trace this call precisely) — retail never
|
|
// shows the Summary text there. Every Help* handler below was located
|
|
// by name in acclient_2013_pseudo_c.txt, its exact byte extent (this
|
|
// function's start VA to the next function's start VA, read from the
|
|
// pseudo-C's own function-header addresses) swept for `push imm32`
|
|
// string literals against the PDB-paired
|
|
// C:\Users\erikn\Downloads\acclient.exe (verified MATCH via
|
|
// check_exe_pdb.py), and the resulting branch assignment confirmed by
|
|
// reading the actual decompiled if/else shape rather than guessing
|
|
// from string length or address order — those two heuristics
|
|
// DISAGREED between HelpDie (Detail at the higher address) and
|
|
// HelpCorpse (Detail at the LOWER address), so neither is trustworthy
|
|
// alone. sweep_weenie_strings.py's stock 800-char cap silently dropped
|
|
// several longer Detail branches (HelpConsent 978 chars, HelpFillComponents
|
|
// 1036, HelpEndurance 2459) as a `None` result with no error — a custom
|
|
// unbounded-length pass (same PE-parsing logic, no cap) recovered them;
|
|
// this is a tool limitation worth fixing generally, not specific to
|
|
// this sweep.
|
|
//
|
|
// <b>CmdHashData help-pointer verification.</b> Several verbs the
|
|
// catalog dispatches (hor/hr, hom/hoa, friends_add, friends_remove,
|
|
// squelch, unsquelch, alh/ah) have no distinctly-NAMED Help* function of
|
|
// their own. `ClientCommunicationSystem::InitializeCommands
|
|
// @0x00581970`'s per-verb block ALWAYS shows the registered help
|
|
// pointer as a literal `nullptr` 4th argument to
|
|
// `CmdHashData::CmdHashData(...)` in Binary Ninja's rendering — a
|
|
// confirmed, systematic decompiler artifact, not per-verb truth: the
|
|
// REAL help pointer is instead stored to a throwaway local
|
|
// (`uint8_t (__stdcall* var_3c_NN)(...) = ClientCommunicationSystem::HelpXxx;`)
|
|
// immediately before the constructor call, which Binary Ninja fails to
|
|
// thread through as the actual argument. Presence of that typed
|
|
// function-pointer local (not a plain `int32_t var_3c_NN = 0;`) is the
|
|
// ground truth: hor/hr/hom/hoa share `HelpHouse` (the SAME function
|
|
// "house"/"hou" use — <see cref="HouseOverview"/> already covers it),
|
|
// friends_add/friends_remove share `HelpFriends` (same as "friends"),
|
|
// squelch/unsquelch share `HelpSquelch`, and alh/ah share
|
|
// `HelpAllegiance` (the SAME function "allegiance"/"all" use — <see
|
|
// cref="AllegianceOverview"/> already covers it). Four verbs
|
|
// (index/clist/on/off) genuinely DO have a plain `int32_t var_3c_NN = 0;`
|
|
// local with no preceding Help-pointer assignment — CONFIRMED NULL,
|
|
// not merely undecoded: retail's own `DoHelp` skips straight to its
|
|
// "Unknown command" fallback for these (the `if (eax_35 != 0)` guard
|
|
// around the help-pointer call), even though the verb itself dispatches
|
|
// fine. <see cref="CatalogVerbsWithNoRetailHelp"/> names them so
|
|
// <c>ChatCommandRouter</c> can reproduce that exact behavior instead of
|
|
// falling back to the catalog's invented summary.
|
|
//
|
|
// <c>messagetypes</c>/<c>message_types</c>/<c>msgtypes</c>/<c>msg_types</c>
|
|
// were the one leaf verb not extracted at the consolidated-review round:
|
|
// `HelpMessageTypes @0x0056e5d0` calls
|
|
// `gmCCommunicationSystem::GetListofSquelchChannels(arg4)` to build its
|
|
// text from a live enum table at runtime, not a static string. Round 4
|
|
// (2026-08-10) ported that live construction for real instead of
|
|
// leaving an acdream summary in its place — see
|
|
// <see cref="MessageTypesDetail"/> and <see cref="BuildMessageTypesDetail"/>
|
|
// below for the full trace (IsLegalChannel's fixed whitelist +
|
|
// LogTextTypeToString's fixed name table + the exact join/wrap format,
|
|
// all read directly off the decompiled bodies). Because both the
|
|
// whitelist and the name table are fixed data, the resulting text is
|
|
// itself a compile-time constant — this is "port the construction," not
|
|
// "guess the result."
|
|
//
|
|
// Coverage this round: 43 of the 47 catalog Definitions have a verbatim
|
|
// Detail override (<see cref="CatalogVerbDetailByVerb"/>, several
|
|
// sharing one extraction per the CmdHashData note above, plus
|
|
// messagetypes' real construction), and 4 are CONFIRMED-NULL
|
|
// (<see cref="CatalogVerbsWithNoRetailHelp"/>) — zero remain an
|
|
// unverified acdream summary. <c>RetailCommandHelpTableTests</c> pins
|
|
// these counts so a future extraction pass (or an accidental
|
|
// regression) is caught.
|
|
public const string LifestoneDetail =
|
|
"@lifestone - Returns you to the last lifestone you used without killing you.\n";
|
|
|
|
public const string MarketplaceDetail =
|
|
"@marketplace - Teleports you to the Marketplace of Dereth.\n";
|
|
|
|
public const string PkArenaDetail =
|
|
"@pkarena - Teleports you to the PK Arena. You must be PK to use this command.\n";
|
|
|
|
public const string PkLiteArenaDetail =
|
|
"@pklarena - Teleports you to the PKL Arena. You must be PKL to use this command.\n";
|
|
|
|
public const string PkLiteDetail =
|
|
"@pklite - Sets your status to Player Killer Lite (PK Lite). PK Lite characters can attack other PK Lite characters. They cannot, however, attack Player Killer (PK) characters. PK Lite characters operate under the same combat rules as PK characters, except that if you are killed in a PK Lite battle, you will not accrue vitae and you will not drop any coins or items. Only Non-Player Killers may use this command to enter PK Lite. Dying in a PK Lite battle and logging off will restore your status to Non-Player Killer.\n";
|
|
|
|
public const string AgeDetail =
|
|
"@age - Displays your total gameplay time.\n";
|
|
|
|
public const string BirthDetail =
|
|
"@birth - Displays when your character was created.\n";
|
|
|
|
public const string FrameRateDetail =
|
|
"@framerate - Toggles the framerate display.\n";
|
|
|
|
public const string LockUiDetail =
|
|
"@lockui - Toggles the locked state of the UI layout.\n";
|
|
|
|
public const string VersionDetail =
|
|
"@version - Tells you what version of the software you are using.\n";
|
|
|
|
public const string LocDetail =
|
|
"@loc - Displays your current position in your chat window. Use this information when you wish to submit a bug report.\n";
|
|
|
|
public const string CorpseDetail =
|
|
"@corpse - Displays the location of your last outdoor death. Even if your corpse has disappeared or if you have subsequently died indoors, typing this command will display your last outdoor corpse location.\n";
|
|
|
|
public const string DieDetail =
|
|
"@die - If you wish to kill your character and leave a corpse, you may use the @die command. This will result in your character's death, you will leave behind a corpse with some of your items, and you will appear at your lifestone. If you wish to travel to your lifestone without leaving behind a corpse, you may use the @lifestone command.\n";
|
|
|
|
public const string ClearDetail =
|
|
"@clear - Clears the chat box of all text.\n";
|
|
|
|
public const string SaveUiDetail =
|
|
"@saveui <filename> - Saves the current user interface layout to disk using the provided file name. If no file name is provided the layout is saved with a name that is unique for your server, character and resolution.\n";
|
|
|
|
public const string LoadUiDetail =
|
|
"@loadui <filename> - Loads a previously saved user interface layout from disk using the provided file name";
|
|
|
|
public const string SaveAutoUiDetail =
|
|
"@saveautoui - Stores the current layout to a character and resolution specific file. This layout will automatically be used when the resolution changes for this character to the current size.\n";
|
|
|
|
public const string LoadAutoUiDetail =
|
|
"@loadautoui - Forces a previously saved layout to load for this user and resolution.";
|
|
|
|
public const string AfkDetail =
|
|
"@afk - Turns on AFK (away-from-keyboard) mode. When set to AFK, other players that send you directed chatyou will receive a customizable message that your are not currently at the keyboard.\n@afk on - Turns on AFK mode. When set to AFK, other players that send you directed chatyou will receive a customizable message that your are not currently at the keyboard.\n@afk off - Turn off AFK mode.\n@afk msg <message> - Set the message that will be sent to players that send you directed chat while you are in AFK mode. Issuing \"@afk msg\" with no message will set your AFK message back to the default. Your custom AFK message is limited to 192 characters.\n";
|
|
|
|
public const string ConsentDetail =
|
|
"The @consent commands allow you to display and manage your corpse-looting consent list. This list lets you control whether others may permit you to loot their corpse and also allows you to monitor who has given you permission. You may have a maximum of 20 separate permissions at any given time. You will not be able to loot a corpse that was the victim of a player killer, even if its owner has given you permission. Also, players who have squelched you are not able to permit you to loot their corpse. Note that you can toggle your consent on/off via the Character Options panel as well as through these commands.\n@consent on - Turns on your ability to accept permissions from other players.\n@consent off - Turns off your ability to accept permissions from other players.\n@consent who - Lists those who have given you permission to loot their corpses.\n@consent remove <name> - Removes the permission a player granted to you.\n@consent clear - Clears your entire consent list.\n\n";
|
|
|
|
public const string EmoteDetail =
|
|
"The @emote command causes your character to emote some text, by performing an action in the third person. For example, if you typed the following while logged in as a character named Arville:\n @emote looks around the town curiously.\nthen the chat windows of everyone around you would display:\n Arville looks around the town curiously.\nYou can use any of these shorter forms of the command as well:\n @e <text>\n @em <text>\n ; <text>\n : <text>\n\nYou can also use a variety of standard emotes. These emotes come with special animations as well as text. Type @emotes to see a list.\n\n";
|
|
|
|
public const string EmoteListDetail =
|
|
"Standard Emotes:\nNote: These commands should be bound on either side by asterisks. (Example: *wave*)\nShakeFist; Beckon; BeSeeingYou; BlowKiss; BowDeep; ClapHands; Cry; Laugh; Nod; Point; Shrug; Wave; Akimbo; HeartyLaugh; Salute; TapFoot; WaveHigh; WaveLow; Yawn; Stretch; Cringe; Kneel; Plead; Shiver; Shoo; Slouch; Spit; Surrender; Woah; Winded; YMCA; Eat; Drink; Teapot; Pray; Mock; Cheer; Helper; Warm Hands; Scratch Head; Shake Head\n\n";
|
|
|
|
public const string FriendsDetail =
|
|
"Every time someone on your friends list logs in or out, you will receive notification. In addition, you can query the online status of your friends list at any time. Your friends list can contain up to 50 characters.\n@friends - Shows all your current friends and indicates if any of them are online.\n@friends online - Shows your current online friends.\n@friends add <name> - Adds a character to your friends list.\n@friends remove <name> - Removes a character from your friends list.\n@friends remove -all - Clears your friends list.\n@friends old - Shows the characters who were on your old-style friends list prior to the January 2006 update, so you can move them to your new-style friends list if necessary.\n";
|
|
|
|
public const string SquelchDetail =
|
|
"The @squelch commands let you block out messages from specific characters or players. The @unsquelch commands lets squelched messages reach you again. Use the options on these commands to squelch all message types or just some types of messages; one character or an entire account. You may have up to 32 players squelched at once. Note that NPCs cannot be permanently squelched.\n\n@squelch - Shows the current list of squelched characters.\n@squelch [-account] <name> - Squelches all messages from a character. With the account flag, this command also stops everything except normal chat coming from the target's other characters.\n@squelch [-message_type] <character> - This will filter out all text messages of a certain type from a specific character. For example, the following will filter out all tell messages from Oswald:\n Example: @squelch -tell Oswald.\n@squelch -reply [-account] [-message_type] - This filters out all text messages from whoever last tell'd you. You may also use the -account flag and/or limit the squelch by indicating specific message types. For example, this will filter out all tell messages from the account of Oswald, assuming that Oswald was the last person who sent you an @tell:\n Example: @squelch -reply -account -tell\n\n@unsquelch - Shows the current list of squelched characters.\n@unsquelch <name> - Removes all squelches from a character, including account squelch.\n@unsquelch [-message_type] <character> : This allows text messages of type message_type to come from a squelched character. For example the following allows assessment messages from a character name Oswald:\n Example: @unsquelch -assessment Oswald\n@unsquelch -reply [-account] [-message_type] : This allows text messages of type message_type from whoever last sent you an @tell. For example, the following will allow any character on Oswald's account to once again send you @tells, assuming that Oswald was the last person who sent you an @tell:\n Example: @squelch -reply -account -tell\n\nType @messagetypes for a complete list of message types.\n";
|
|
|
|
public const string FilterDetail =
|
|
"The @filter commands filter out all incoming messages of a certain type. Type @messagetypes to see a list of the message types that you can filter.\n@filter - List all the filters currently in place.\n@filter <-message_type> - Filters out all incoming messages of a specific type. For example, the following will filter out all spellcasting text: \n Example: @filter -spellcasting\n@filter -all - Filters out all incoming messages of all types.\n\n";
|
|
|
|
public const string UnfilterDetail =
|
|
"The @unfilter commands remove specific filters from your incoming messages. For a complete list of message types that you can filter, type @help messagetypes.\n@unfilter <-message_type> - Removes filters on incoming messages of a specific type. For example, the following allows spellcasting text to resume:\n Example: @unfilter -spellcasting\n@unfilter -all - Removes all filters on incoming messages of all types.\n";
|
|
|
|
public const string FillCompsDetail =
|
|
"The @fillcomps command assists in the bulk purchase of spell components. It is the sole interface for filling the buy list, which is the column of red zeros to the right in your components panel. To designate which components you would like to buy, change the zeros to the number of each component you would like to buy. The types of components you can buy are scarabs, herbs, powders, potions, and talismans.\n\nThis is the proper syntax: @fillcomps <component type> <pyreal value>\n\n@fillcomps - Fills the buy list with all of the components that are desired.\n@fillcomps <component type> - Fills the buy list with all of the components of the given type.\n@fillcomps <pyreal value> - Fills the buy list with all of the components until the total price of the components exceeds the given value.\n@fillcomps <component type> <pyreal value> - Fills the buy list with all of the components of the given type until the total price of components exceeds the given value.\n@fillcomps clear - Sets the requested amount for all components to zero.\n";
|
|
|
|
public const string EnduranceDetail =
|
|
"The endurance attribute has a number of abilities tied to it.\nFirst, some combination of strength and endurance (with endurance being more important) now allows one to regenerate hit points at a faster rate the higher one's endurance is. This bonus is in addition to any regeneration spells one may have placed upon themselves. This endurance regeneration bonus caps at around 110%.\nSecond, the higher a player's Endurance, the less stamina one uses while attacking. This benefit is tied to Endurance only, and it caps out at around 50% less stamina used per attack. The minimum stamina used per attack remains one.\nThird, the higher a player's Endurance, the more likely they are not to use a point of stamina to successfully evade a missile or melee attack. A player is required to have Melee Defense for melee attacks or Missile Defense for missile attacks trained or specialized in order for this specific ability to work. This benefit is tied to Endurance only, and it caps out at around a 75% chance to avoid losing a point of stamina per successful evasion.\nFourth, some combination of strength and endurance (the two are roughly of equivalent importance) now allows one to partially resist drain and harm attacks, up to a maximum of roughly 50%.\nFifth, some combination of strength and endurance (the two are roughly of equivalent importance) now allows one to have a level of \"natural resistances\" to the 7 damage types, the same as a certain level of life protections. This caps out at a 50% resistance (the equivalent to level 5 life prots) to these damage types. This resistance is not additive to life protections: higher level life protections will overwrite these natural resistances, although life vulns will take these natural resistances into account, if the player does not have a higher level life protection cast upon him.\nThe natural resistances, drain resistances, and regeneration rate info are now visible on the Character Information Panel, in what was once the Burden panel. This panel now displays the above three Endurance benefits, the burden info, as well as information about your age, birth date, and number of deaths.\nThe 5 categories for the endurance benefits are, in order from lowest benefit to highest: Poor, Mediocre, Hardy, Resilient, and Indomitable, with each range of benefits divided up equally amongst the 5 (e.g. Poor describes having anywhere from 1-10% resistance against drain health attacks, etc.).\n";
|
|
|
|
public const string SpeakerDetail =
|
|
"@speaker - No longer used, see @allegiance officer for a similar command.\n";
|
|
|
|
public const string TitleDetail =
|
|
"@title <new title> - Sets the title of the popup chat window.\n";
|
|
|
|
public const string ChatToggleDetail =
|
|
"@chat <on/off> - Sets whether or not you receive normal chat. When set to \"off\", you will no longer receive any spoken speech (normal chat). However, you will still receive tells.\n";
|
|
|
|
public const string NoTellDetail =
|
|
"@notell <on/off> - Sets whether or not you receive @tells. When set to \"on\", you will not receive any tells.\n";
|
|
|
|
public const string JoinChatDetail =
|
|
"@join <channel tag> - Allows you to hear and speak on the given channel.\n";
|
|
|
|
public const string LeaveChatDetail =
|
|
"@leave <channel tag> - Prevents you from hearing or speaking on the given channel.\n";
|
|
|
|
public const string PermitDetail =
|
|
"The @permit command gives or revokes corpse-looting permissions to other players. You can permit other players to loot any one of your corpses. You may not @permit a player again until he or she has looted your corpse. Permissions expire either after one hour or when the permitted player logs off. If you were killed by a player killer, no one can loot your corpse except you or your killer, even if you give someone else permission.\n@permit add <name> - Allows another player to loot your corpse.\n@permit remove <name> - Removes permission to access your corpse from the named character.\nType @help consent for more details on corpse looting.\n";
|
|
|
|
public const string HslistDetail =
|
|
"@hslist <house type> - Lists the number and, if appropriate, positions of houses currently available for purchase. Types include: Apartment, Cottage, Villa, Mansion\n";
|
|
|
|
// ── messagetypes: a real construction, not a hand-authored string ───
|
|
//
|
|
// Retail's HelpMessageTypes @0x0056E5D0 -> GetListofSquelchChannels
|
|
// @0x00589EB0 -> ConvertSQToPString @0x00589A80 (the `arg3 != 0`
|
|
// "enumerate every legal channel" branch) chain, read directly:
|
|
// 1. Iterate i = 0..0x21 in ascending order.
|
|
// 2. Keep i only if LogTextTypeEnumMapper::IsLegalChannel(i) @
|
|
// 0x006AFF40 -- a literal switch over exactly 14 ids: 0x02, 0x03,
|
|
// 0x06, 0x07, 0x0C, 0x10, 0x11, 0x12, 0x13, 0x15, 0x16, 0x17,
|
|
// 0x18, 0x19. (The SquelchInfo the real function iterates is a
|
|
// SYNTHETIC all-squelched object built solely to walk every legal
|
|
// id -- SquelchInfo::SquelchInfo(..., 1) -- not the player's
|
|
// actual squelch state, so this is genuinely a fixed enumeration,
|
|
// not a live/variable one.)
|
|
// 3. Get each kept id's display name via
|
|
// LogTextTypeEnumMapper::LogTextTypeToString @0x006AFF90 -- also a
|
|
// literal switch, read directly (not the C# enum member spelling,
|
|
// which differs by underscores for two entries): Speech, Tell,
|
|
// Combat, Magic, Emote, Appraisal, Spellcasting, Allegiance,
|
|
// Fellowship, Combat_Enemy, Combat_Self, Recall, Craft, Salvaging.
|
|
// 4. Join with ", " (data_0x794334, swept) and wrap in
|
|
// "Squelch channels are as follows:\n %s\n"
|
|
// (data_0x7e2fb8, swept off GetListofSquelchChannels' own
|
|
// sprintf call).
|
|
// Both the whitelist and the name table are fixed data, so the joined
|
|
// result is itself a compile-time constant -- SquelchLegalChannels +
|
|
// BuildMessageTypesDetail port the ENUMERATION rather than
|
|
// hand-authoring the already-known joined string, so a future
|
|
// whitelist change is a one-line table edit.
|
|
private static readonly (RetailLogTextType Type, string Name)[] SquelchLegalChannels =
|
|
{
|
|
(RetailLogTextType.Speech, "Speech"),
|
|
(RetailLogTextType.Tell, "Tell"),
|
|
(RetailLogTextType.Combat, "Combat"),
|
|
(RetailLogTextType.Magic, "Magic"),
|
|
(RetailLogTextType.Emote, "Emote"),
|
|
(RetailLogTextType.Appraisal, "Appraisal"),
|
|
(RetailLogTextType.Spellcasting, "Spellcasting"),
|
|
(RetailLogTextType.Allegiance, "Allegiance"),
|
|
(RetailLogTextType.Fellowship, "Fellowship"),
|
|
(RetailLogTextType.CombatEnemy, "Combat_Enemy"),
|
|
(RetailLogTextType.CombatSelf, "Combat_Self"),
|
|
(RetailLogTextType.Recall, "Recall"),
|
|
(RetailLogTextType.Craft, "Craft"),
|
|
(RetailLogTextType.Salvaging, "Salvaging"),
|
|
};
|
|
|
|
private static string BuildMessageTypesDetail() =>
|
|
"Squelch channels are as follows:\n "
|
|
+ string.Join(", ", Array.ConvertAll(SquelchLegalChannels, c => c.Name))
|
|
+ "\n";
|
|
|
|
/// <summary>
|
|
/// Retail's real <c>/help messagetypes</c> text (and its 3 aliases),
|
|
/// ported from the live construction retail itself performs -- see the
|
|
/// remarks above <see cref="SquelchLegalChannels"/>. No longer an
|
|
/// acdream-authored summary.
|
|
/// </summary>
|
|
public static readonly string MessageTypesDetail = BuildMessageTypesDetail();
|
|
|
|
private static readonly FrozenDictionary<string, string> CatalogVerbDetailByVerb =
|
|
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
// CT-B4 (2026-08-21): "log" became a catalog verb, so its retail
|
|
// help has to be reachable through the CATALOG path too — the
|
|
// catalog's own one-line summary is acdream-authored, and showing
|
|
// that in place of retail's text is exactly what this table exists
|
|
// to prevent.
|
|
["log"] = Log,
|
|
["day"] = Day,
|
|
["render"] = Render,
|
|
["motd"] = Motd,
|
|
["lifestone"] = LifestoneDetail,
|
|
["lif"] = LifestoneDetail,
|
|
["ls"] = LifestoneDetail,
|
|
["marketplace"] = MarketplaceDetail,
|
|
["mar"] = MarketplaceDetail,
|
|
["mp"] = MarketplaceDetail,
|
|
["pkarena"] = PkArenaDetail,
|
|
["pka"] = PkArenaDetail,
|
|
["pklarena"] = PkLiteArenaDetail,
|
|
["pla"] = PkLiteArenaDetail,
|
|
["pklite"] = PkLiteDetail,
|
|
["pkl"] = PkLiteDetail,
|
|
// hor/hr/hom/hoa share HelpHouse with "house"/"hou" — see the
|
|
// CmdHashData note in the class remarks above.
|
|
["hor"] = HouseOverview,
|
|
["hr"] = HouseOverview,
|
|
["hom"] = HouseOverview,
|
|
["hoa"] = HouseOverview,
|
|
["age"] = AgeDetail,
|
|
["birth"] = BirthDetail,
|
|
["framerate"] = FrameRateDetail,
|
|
["lockui"] = LockUiDetail,
|
|
["version"] = VersionDetail,
|
|
["loc"] = LocDetail,
|
|
["corpse"] = CorpseDetail,
|
|
["cor"] = CorpseDetail,
|
|
["die"] = DieDetail,
|
|
["clear"] = ClearDetail,
|
|
["saveui"] = SaveUiDetail,
|
|
["loadui"] = LoadUiDetail,
|
|
["saveautoui"] = SaveAutoUiDetail,
|
|
["loadautoui"] = LoadAutoUiDetail,
|
|
["afk"] = AfkDetail,
|
|
["consent"] = ConsentDetail,
|
|
["e"] = EmoteDetail,
|
|
["em"] = EmoteDetail,
|
|
["emote"] = EmoteDetail,
|
|
["me"] = EmoteDetail,
|
|
["emotes"] = EmoteListDetail,
|
|
// friends/friends_add/friends_remove share HelpFriends — see the
|
|
// CmdHashData note in the class remarks above.
|
|
["friends"] = FriendsDetail,
|
|
["friends_add"] = FriendsDetail,
|
|
["friends_remove"] = FriendsDetail,
|
|
// squelch/unsquelch share HelpSquelch (and its own concatenated
|
|
// HelpAdvancedSquelch/HelpAdvancedUnSquelch delegation) — see
|
|
// the CmdHashData note in the class remarks above.
|
|
["squelch"] = SquelchDetail,
|
|
["unsquelch"] = SquelchDetail,
|
|
["filter"] = FilterDetail,
|
|
["unfilter"] = UnfilterDetail,
|
|
// Round 4 (2026-08-10): messagetypes/message_types/msgtypes/
|
|
// msg_types now resolve to MessageTypesDetail, the ported live
|
|
// construction — see the remarks above SquelchLegalChannels.
|
|
["messagetypes"] = MessageTypesDetail,
|
|
["message_types"] = MessageTypesDetail,
|
|
["msgtypes"] = MessageTypesDetail,
|
|
["msg_types"] = MessageTypesDetail,
|
|
["fillcomps"] = FillCompsDetail,
|
|
["endurance"] = EnduranceDetail,
|
|
["speaker"] = SpeakerDetail,
|
|
["title"] = TitleDetail,
|
|
["chat"] = ChatToggleDetail,
|
|
["notell"] = NoTellDetail,
|
|
["join"] = JoinChatDetail,
|
|
["leave"] = LeaveChatDetail,
|
|
["permit"] = PermitDetail,
|
|
["hslist"] = HslistDetail,
|
|
// alh/ah share HelpAllegiance with "allegiance"/"all" — see the
|
|
// CmdHashData note in the class remarks above.
|
|
["alh"] = AllegianceOverview,
|
|
["ah"] = AllegianceOverview,
|
|
}.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase);
|
|
|
|
/// <summary>
|
|
/// Retail-registered catalog leaf verbs confirmed (via the CmdHashData
|
|
/// help-pointer note in the class remarks above) to register with a
|
|
/// NULL help function pointer. Retail's own <c>DoHelp</c> skips its
|
|
/// help-callback branch entirely for these and falls straight to the
|
|
/// generic <see cref="UnknownCommand"/> text — the SAME text an
|
|
/// unregistered verb gets — even though the verb dispatches normally
|
|
/// for ordinary (non-help) use. <c>ChatCommandRouter</c> must check
|
|
/// this BEFORE consulting either <see cref="CatalogVerbDetailByVerb"/>
|
|
/// or the catalog's own summary, or it would show acdream-invented
|
|
/// help text retail never displays.
|
|
/// </summary>
|
|
public static readonly FrozenSet<string> CatalogVerbsWithNoRetailHelp =
|
|
new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "index", "clist", "on", "off" }
|
|
.ToFrozenSet(StringComparer.OrdinalIgnoreCase);
|
|
|
|
/// <summary>
|
|
/// Retail Detail_HelpType(2) text for a <see cref="RetailClientCommandCatalog"/>
|
|
/// leaf verb — see the class remarks' "Catalog leaf-verb Detail
|
|
/// extraction" section for the recovery method and coverage count.
|
|
/// Callers should check <see cref="CatalogVerbsWithNoRetailHelp"/>
|
|
/// FIRST (a confirmed-null verb must never reach this lookup, since a
|
|
/// stale entry here would silently show text retail never displays for
|
|
/// it), then this method, then fall back to
|
|
/// <see cref="RetailClientCommandCatalog.TryGetHelpText"/> for the
|
|
/// verbs not yet extracted.
|
|
/// </summary>
|
|
public static bool TryGetCatalogVerbDetailText(string verb, out string detailText) =>
|
|
CatalogVerbDetailByVerb.TryGetValue(verb.TrimEnd(','), out detailText!);
|
|
|
|
private static readonly FrozenDictionary<string, string> ByVerb =
|
|
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
["say"] = "@say <text> - Speaks the text aloud to nearby players.",
|
|
["s"] = "@say <text> - Speaks the text aloud to nearby players.",
|
|
["tell"] = Tell,
|
|
["t"] = Tell,
|
|
["send"] = Tell,
|
|
["whisper"] = Tell,
|
|
["w"] = Tell,
|
|
["reply"] = Reply,
|
|
["r"] = Reply,
|
|
["rp"] = Reply,
|
|
["retell"] = Retell,
|
|
["rt"] = Retell,
|
|
["mr"] = MonarchReply,
|
|
["pr"] = PatronReply,
|
|
["day"] = Day,
|
|
["log"] = Log,
|
|
["render"] = Render,
|
|
["motd"] = Motd,
|
|
["commands"] = CommandsGroupDetail,
|
|
["allegiances"] = AllegiancesGroupDetail,
|
|
["channels"] = ChannelsGroupDetail,
|
|
["chatting"] = ChattingGroupDetail,
|
|
["death"] = DeathGroupDetail,
|
|
["status"] = StatusGroupDetail,
|
|
["text"] = TextGroupDetail,
|
|
// Channel verbs. Campaign CH user-gate round 2, item 3: 7 of
|
|
// these are now VERBATIM retail text (each its own
|
|
// HelpTurbineChat_Xxx function, decomp §5 sweep,
|
|
// acclient_2013_pseudo_c.txt:387294-387411/387410 area,
|
|
// addresses 0x577620-0x577850) — the "Also: @alias" suffix is
|
|
// retail's own text, not an acdream addition. The remaining
|
|
// ones (f/fellow(s)/fellowship/g/group/party, m/monarch,
|
|
// p/patron, v/vassal(s), c/covassal(s)/co-vassals) still route
|
|
// through the unresolved HelpStupidChannelHack pooled-string
|
|
// mechanism (see the class remarks above) and stay
|
|
// acdream-authored summaries — an honest gap, not a silent one.
|
|
["f"] = "Sends text to your Fellowship channel.",
|
|
["fellow"] = "Sends text to your Fellowship channel.",
|
|
["fellows"] = "Sends text to your Fellowship channel.",
|
|
["fellowship"] = "Sends text to your Fellowship channel.",
|
|
["g"] = "Sends text to your Fellowship channel.",
|
|
["group"] = "Sends text to your Fellowship channel.",
|
|
["party"] = "Sends text to your Fellowship channel.",
|
|
["a"] = "@a - Sends a message to your Allegiance. Also: @guild, @gu",
|
|
["guild"] = "@a - Sends a message to your Allegiance. Also: @guild, @gu",
|
|
["gu"] = "@a - Sends a message to your Allegiance. Also: @guild, @gu",
|
|
["ab"] = "Broadcasts text to your entire allegiance (monarch/speaker permission). Also @allegiance broadcast.",
|
|
["general"] = "@general - Sends a message to the global General chat channel. Also: @cg",
|
|
["cg"] = "@general - Sends a message to the global General chat channel. Also: @cg",
|
|
["trade"] = "@trade - Sends a message to the global Trade chat channel. Also: @ct",
|
|
["ct"] = "@trade - Sends a message to the global Trade chat channel. Also: @ct",
|
|
["lfg"] = "@lfg - Sends a message to the global Looking For Group (LFG) chat channel. Also: @clfg",
|
|
["clfg"] = "@lfg - Sends a message to the global Looking For Group (LFG) chat channel. Also: @clfg",
|
|
["roleplay"] = "@roleplay - Sends a message to the global Roleplay chat channel. Also: @crp",
|
|
["crp"] = "@roleplay - Sends a message to the global Roleplay chat channel. Also: @crp",
|
|
["society"] = "@society - Sends a message to the your Society chat channel. Also: @soc",
|
|
["soc"] = "@society - Sends a message to the your Society chat channel. Also: @soc",
|
|
["olthoi"] = "@olthoi - If you are an Olthoi, sends a message to the global Olthoi chat channel. Also: @o",
|
|
["o"] = "@olthoi - If you are an Olthoi, sends a message to the global Olthoi chat channel. Also: @o",
|
|
["m"] = "Sends text to your Monarch.",
|
|
["monarch"] = "Sends text to your Monarch.",
|
|
["p"] = "Sends text to your Patron.",
|
|
["patron"] = "Sends text to your Patron.",
|
|
["v"] = "Sends text to your Vassals.",
|
|
["vassal"] = "Sends text to your Vassals.",
|
|
["vassals"] = "Sends text to your Vassals.",
|
|
["c"] = "Sends text to your Co-vassals.",
|
|
["covassal"] = "Sends text to your Co-vassals.",
|
|
["covassals"] = "Sends text to your Co-vassals.",
|
|
["co-vassals"] = "Sends text to your Co-vassals.",
|
|
}.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase);
|
|
|
|
public static bool TryGetHelpText(string verb, out string helpText) =>
|
|
ByVerb.TryGetValue(verb.TrimEnd(','), out helpText!);
|
|
}
|