The user set the goal: complete retail parity for the chat system AND the chat
GUI, not just the green clickable name that started the review. Plan restated
around that bar, with a definition of done — every retail behaviour either
implemented or carrying a divergence-register row, every user-visible surface
covered by a test, and the digest/ISSUES describing reality.
Slices regrouped into A (the tagged-text capability, a strict chain where
nothing is visible until A4), B (system behaviours), C (GUI), D (hygiene), plus
the research still owed before specific slices and what is deliberately out of
scope.
Four items joined the plan that the original six lanes did not own, because
they fell between lanes:
- the /r, /t, /tell text-replacement macro (the commands work; retail's
VISIBLE expansion to "@tell {LastTeller}, " does not exist)
- FilterLanguage, which is a decorative toggle: we store it, ship the bit and
show it in Options, and never actually filter anything
- the plain-text session chat log retail writes and we do not
- the option-gated timestamp prefix
Also corrects the CH3 command-registry research note. Its "acdream status"
columns are from before slice CH4 and list 13 verbs as MISSING that have all
since been added — cg, soc, o, co-vassals, fellows, group, party, vassal, ab,
guild, ct, clfg, crp — and its DIVERGENT row for /g is likewise stale: acdream
maps /g to Fellowship, matching retail, confirmed against the live client
today. The retail side of that document is still the authority; only the
columns describing us were wrong. They misled this session's investigation,
which is exactly why the banner says to verify against ChatInputParser.cs.
Nothing implemented.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
7.2 KiB
Campaign CT — complete chat parity (system + GUI)
Status: PROPOSED (2026-08-21). Not started.
Goal, set by the user 2026-08-21: complete retail parity for the chat system AND the chat GUI. Not "fix the green name" — that was the symptom that started the review. The bar is that a retail player sitting down in front of acdream's chat window finds nothing missing and nothing behaving differently.
Campaign CH (2026-08-09, closed user-accepted) landed colours, side channels,
the 152-verb command registry, the window shell and verbatim /help. CT is
the pass that closes what CH did not reach.
Research notes (all 2026-08-21): chat-texttag-model.md,
chat-tagged-name-composition.md, chat-tag-click-dispatch.md,
retail-chat-window-ui.md, acdream-text-stack-audit.md,
acdream-chat-ui-audit.md.
Definition of done
- Every retail chat behaviour is either implemented, or has a divergence- register row saying why not.
- Every user-visible chat surface has a test that would catch its regression.
- The chat digest and
docs/ISSUES.mddescribe reality (both are stale today).
What the review established
The green clickable name is a TEXT-STACK gap, not a chat gap
The client sprintfs literal markup into the line —
<Tell:IIDString:{iid}:{name}>{name}<\Tell> says, "{text}", closing marker a
literal backslash — and UIElement_Text::InqGlyphs @0x00468EA0 parses the
brackets while appending, calling TextTagFactory::MakeTag @0x00478480. Tags
attach per glyph; a "run" is emergent (adjacent glyphs with equal tag
pointers). A glyph takes the tag colour (property 0x1D) only when a tag is
open AND its m_type == 0x10000001, else the line colour (0x1B). Only
senders with a GUID in 0x50000001..0x6FFFFFFF are tagged.
Colour measured from the installed dats (LayoutDump --colors), chat
0x2100006F / transcript 0x10000011: P0x1B = RGB(204,204,204),
P0x1D = RGB(0,178,0). The tag colour is per-ELEMENT and authored, while
the line colour on that same element comes from the runtime chat table —
filing "tag green" into the LogTextType table would put it in the wrong place.
Click: UIElement_Text::MouseUp @0x004694F0 → DeterminePositionFromXY @0x004688F0 → GlyphList::InqGlyph @0x00473430 → virtual HandleClick at
tag-vtable +0x14 → gmMainChatUI::RecvNotice_TextTag_IIDStringClick @0x004CCE10 → ChatInterface::StartTell @0x004F41F0, which writes
"@tell {Name}, ", takes focus, and shows the entry bar. Clicking a name
always opens a TELL — fellowship, allegiance, patron/vassal and named-channel
lines all embed the same markup. No hover effect.
Our side is closer than feared
UiText already draws multi-coloured runs (TextRun/RunsProvider, used
by the character stat panel); it is gated to OneLine == true. The draw path
needs no renderer work — arbitrary pen X, substring measurement — and
UiText.HitChar already resolves a click to (line, column). The blocker is
that ChatVM.RecentLinesDetailed() drops Sender/SenderGuid one step before
the renderer, though ChatEntry carries them the whole way.
The command registry is already at parity
All 13 verbs the CH3 research note lists as MISSING were closed by CH4 and
verified present 2026-08-21 (cg, soc, o, co-vassals, fellows,
group, party, vassal, ab, guild, ct, clfg, crp). /g correctly
resolves to Fellowship, confirmed against the live retail client. That note's
"acdream status" columns are stale and now carry a correction banner.
Slices
Group A — the tagged-text capability (strict chain, A1→A5)
Nothing is user-visible until A4.
- CT-A1 Multi-line text elements carry coloured runs. Additive; the ~50
files using
Lineare untouched. No behaviour change. - CT-A2 Parse the tag markup into runs with a tag payload, including retail's rule that an unparseable bracket closes the open tag. Pure, unit- testable, no UI.
- CT-A3 Stop flattening: carry sender name + guid through
ChatVMinto spans, and compose retail's markup in the speech handlers behind the player-GUID-range gate. - CT-A4 Apply the authored
0x1Dtag colour when a tag is open and its type matches. Names turn green. - CT-A5 Sub-line hit-testing and
StartTell. Names become clickable.
Group B — chat SYSTEM behaviours
- CT-B1 Bound the transcript: 10,000 chars, trim to ~7,500 preferring a
newline boundary (
TruncateChatLog @0x004F4290). Today it grows for the life of the session — a slow leak, not only a fidelity gap. - CT-B2 Text-replacement macros: typing
/r,/t,/tellrewrites the input to@tell {LastTeller},on the space keypress (HandleTextReplacements @0x004F50D0). The commands already work; the visible expansion does not exist. - CT-B3
FilterLanguage: retail runs chat text through a taboo table and substitutes (PlayerModule::FilterLanguage+TabooTableAdaptor:: CheckCensorsWinsideAddTextToScroll). We store the option, ship the bit, show it in Options — and never filter. A decorative toggle. - CT-B4 The plain-text session chat log (
ClientSystem::s_pLogFile). We write none. Path and rotation are UNKNOWN — needs research or a live check.
Group C — chat GUI
- CT-C1 Auto-scroll vs unread: retail samples "was at bottom" BEFORE the
line lands; if you had scrolled up it leaves you there and lights the unread
indicator (
0x1000048C), which scrolls to bottom and clears on click. - CT-C2 Escape in the chat input is a complete no-op —
UiFieldhas noEscapecase, and a focused field also suppresses the input dispatcher's fallback, so there is no clear, no defocus and no hotkey passthrough. - CT-C3 Option-gated timestamp prefix (
%#H:%M:%S, colour index0x0C, grey), gated onPlayerModule::DisplayTimeStamps(). - CT-C4 Input-bar editing parity: clipboard and selection paths
(Ctrl+C/X/V, shift-selection) work but are untested;
ToggleMaximizeand the floating-window Close button have zero coverage.
Group D — hygiene
- CT-D1 Delete the dead ImGui-era
ChatPanel(never constructed since Campaign V deletedAcDream.UI.ImGui), and its three test files, which currently make the real input surface look better covered than it is. - CT-D2 Reconcile the chat digest and
docs/ISSUES.md: #358, #362, #363, #367, #372, #379, #380, #382 are DONE in code but still listed open. #359, #360, #361, #366 remain genuinely open.
Research still owed before the affected slices
- The tag-type roster behind
m_type == 0x10000001— only "Tell" is confirmed; the full set lives in the DATEnumMappercategory0x18. Blocks nothing in Group A, but decides whether other tag shapes exist. - Whether retail's transcript supports text selection distinctly from the entry field (blocks CT-C4's scope).
- The chat log file's path and rotation (blocks CT-B4).
- Whether a chat-specific sound cue exists — a grep came back empty, which is weak evidence, not proof of absence.
Deliberately NOT in scope
Item links and the other three tag shapes (DID, IID, IIDEnum). They have
no listener in the retail build we target, so porting them would be inventing
behaviour. CT-A5's dispatch is generic, so they cost nothing to add later.