Reported as "1d 2h 51sh m" — the running countdown drawing straight through
the d/h/m labels. The readout is authored across the same strip as the three
number boxes, so the strip has to be one thing or the other; I hid the boxes
and left their labels behind.
Retail's ShowEditableTimer @0x00495770 toggles SIX elements, not three:
m_pDaysEditBox AND m_pDaysStaticText, and the same for hours and minutes, plus
the readout inverse. Reading the swap as "hide the inputs" instead of "hide the
input ROWS" is what produced the overlap.
Also settles the Record question the same round raised. Nothing was broken:
indoors, retail's own gid_to_lcoord fails and nothing is recorded, and
UpdateLocation @0x004958F0 only ever formats coordinates already stored — there
is no "you are indoors" message in that function to port. The silence is
faithful, and it is now commented as such rather than left looking like a gap.
JournalPanelLiveBindTests is new and is the test that should have existed
first: it builds the panel from the real DATs, constructs the controllers, and
asserts every button actually receives an OnClick. Every other test so far
checked either the layout or the logic — none of them proved the controller
finds its elements in the real tree, which is where an id typo or a subtree
assumption produces a panel where nothing responds and nothing fails.
The temporary ACDREAM_PROBE_JOURNAL instrumentation is removed; the question it
was added for is answered.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three fixes from the first connected round.
The location readout is authored EDITABLE (0x16), so it builds as a UiField —
not the UiText its "00.0S, 00.0W" placeholder suggests. The controller resolved
it as text, got null, and threw every write away in silence: Record reached the
model and reached the FILE, and never reached the screen. That is exactly what
was reported, and it is a whole class of bug, so the sweep that found it is now
a test over every element all three controllers bind.
The handlers mutated the model and left redrawing to the next frame's Tick.
Retail's ListenToElementMessage @0x004968D0 ends every one of them in Update()
instead — at the moment of the click. The deferred version happened to work in
the client and made the behaviour untestable and a frame late; the notes-page
tests I had not written until now fail against it.
Abandon is wired. "Retail's abandon path is a contract-registry command we have
not ported" was wrong — it is game action 0x0316 with a single contract id, and
ACE replies with the 0x0315 delete QT3 already handles. Nothing is removed
locally, so a refusal leaves the quest visibly intact rather than vanishing it
optimistically and having it reappear on the next full table.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Journal panel now has all three tabs working: contracts from the server,
and a per-character notebook with its searchable index.
Two ported details that a reimplementation would get wrong in a way nobody
notices until they lose work:
Every navigation button commits the current page FIRST. Retail's
ListenToElementMessage @0x004968D0 calls SaveThisPage on the way out of all
five of them, which is why paging away never eats what you just typed. And the
file is written when the notes page is HIDDEN, not only at exit — a crash then
costs at most the page in front of you.
The search is CASE-SENSITIVE across label, title and notes: retail compares
with wcsstr and lowercases neither side. Making it insensitive would be
friendlier and would be a divergence, so it is ported as-is with a test naming
the reason. The double-click window is a full SECOND (m_LastClickTime + 1.0,
@0x00493158) rather than the 500 ms the item-interaction path uses, and firing
it clears the tracker so a third click does not re-open.
Two unlabelled buttons on the notes page turned out to be prev/next: retail
switches on (idElement - 0x10000565), which names them without a caption. The
running-timer readout is authored at the same x as the three day/hour/minute
boxes, so the strip is one or the other — that overlap is the data form of
ShowEditableTimer versus ShowRunningTimer, not a layout bug.
DeltaTimeToString moved out of the contract code into AcDream.Core.Ui. It is
ClientUISystem's, not gmContractsUI's — the journal timer and the contract
repeat countdown both call it, and it only lived under Quests because that was
its first caller. A bridge class to reach it across features would have been
the wrong answer to the same observation.
The journal file lives in the client's data directory rather than beside the
executable, for the same reason the chat log does. Register QJ-1.
Campaign QJ slices 3, 4 and 5 of 5 — code-complete, connected gate owed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>