feat(ui): Campaign OP slice OP6 — the Config tab

Binds the retail Options panel's Config tab (LayoutDesc 0x21000029, 27
authored rows across 6 sections) through OP2's template mechanism and
OP3's per-page OptionPage model, matching the Character/Chat tab
controllers' established pattern.

The row table is transcribed directly from two decompiled sources —
gmConfigUI::InitOptions @0x0049E400 (row order, widget shape, defaults)
and gmClient::InitUIPreferences @0x004035b0 (the complete
UIPreferences::AttachPreference registration: every label/tooltip key,
every slider's real-unit range, every menu's enum choices) — which
resolves the research docs' own "U4" unverified slider-caption pairing:
retail ships ZERO range captions on this tab (every SetSliderLabel call
passes literal string id 0).

Consumer disposition: LIVE — Sound/Ambient volume-trio sliders and their
toggle halves (AudioSettings.SfxDisabled/AmbientDisabled now gate the
already-live engine write; RuntimeSettingsController.SaveAudio newly
pushes into OpenAlAudioEngine on every change, not just at startup),
Resolution/Full Screen (immediate window resize on save). NEXT-LAUNCH
(pre-existing precedent): Sync To Refresh, Field of View. STORE-ONLY
(register rows AP-198/199/200, TS-74 extended): Sound Features/Interface
trio/Play-Only-When-Active, the nine Graphics/Rendering-Quality rows
(Vulkan has no per-feature render knobs), Camera/Input's six rows and
Use Mouse Turning (no persistent mouse-turning camera mode), Chat Font
Face/Size (distinct new fields from the existing live ChatSettings.FontSize).

AudioSettings/DisplaySettings/CameraTurningSettings/ChatSettings each
gain new fields for their slice of the 27 rows, backed by SettingsStore
round-trips. A real bug caught by testing: the scrollbar scope lookup
used the standalone-layout root id (0x100001FF), which does not survive
base-merge into the host-mounted tree — fixed to scope from the tab
host's own page-slot id (0x10000213), matching Chat's established
pattern for the same shared-scrollbar-id hazard (0x10000201, authored by
both the Chat and Config ListBoxes).

30 new tests (27 authored rows register as 30 IOptionRow instances — the
three toggle+slider trios each register two). Full Release suite:
13,107 passed / 4 skipped / 0 failed (was 13,083/4/0 — net +24, the one
existing RuntimeSettingsControllerTests case updated for SaveAudio's new
live-apply call, not a regression).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-11 07:16:35 +02:00
parent 527a3a4056
commit f5ac1742ba
16 changed files with 2188 additions and 30 deletions

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,6 @@
# Campaign OP connected-gate test script
**Status:** OP3, OP4, OP5, and OP7 sections. Later slices (OP6, OP8) append
**Status:** OP3, OP4, OP5, OP6, and OP7 sections. Later slices (OP8) append
their own sections here as they land; the campaign's OP9 closeout gate is
this document complete plus every slice code-complete.
@ -509,6 +509,157 @@ chat windows already read when deciding which lines to show.
---
## OP6 — the Config tab
The Config tab (`0x21000029`) has six sections: **Sound Options** (5 rows —
a menu, three toggle+slider volume trios, one toggle), **Camera Options** (4
rows), **Graphics Options** (7 rows), **Rendering Quality Options** (6
rows), **Input Options** (3 rows), **UI Options** (2 rows) — 27 authored
rows total, byte-verified directly from `gmConfigUI::InitOptions
@0x0049E400` and `gmClient::InitUIPreferences @0x004035b0` (the retail
registration table that supplies every row's label/tooltip/range/enum-
choice — a stronger source than this campaign's own research docs, which
had left the slider-caption pairing explicitly UNVERIFIED; OP6 resolved it:
**retail ships zero range captions on this tab** — do not expect
"Dark/Bright"-style low/high labels on any slider).
### Opening the tab and reading the rows
1. **Open the Options panel (F11) and click the Config tab.** Six section
headers appear top-to-bottom: **Sound Options**, **Camera Options**,
**Graphics Options**, **Rendering Quality Options**, **Input Options**,
**UI Options**. Every row has a label (a menu dropdown, a checkbox, or a
slider — three of the Sound rows are a combined checkbox+slider in one
row). If ANY row shows no label at all, that is a DAT string-resolution
miss worth reporting (the code deliberately renders nothing rather than
invented English when a string fails to resolve).
2. **Scroll the list** via the scrollbar. All 27 rows are reachable; the
list does not clip or overlap the Apply/Reset/Defaults buttons at the
bottom.
### Live rows — audio
3. **Drag the "Sound Effects" slider (first Sound Options trio) while a
sound effect is audibly looping or repeating** (e.g. stand near an
ambient sound source, or trigger a combat/UI sound repeatedly). The
volume should change LIVE, continuously, as you drag — not just on
release.
4. **Check the "Disable Sound Effects" checkbox** (the SAME row's toggle
half). Sound effects should go SILENT immediately, regardless of where
the slider is set. Uncheck it — sound effects resume at the slider's
current level.
5. **Repeat steps 3-4 for the "Ambient Sound" trio** (the second row) using
an ambient loop (wind, water, torches) as your audible test — same live
drag + mute-on-check behaviour.
6. **Note the retail-faithful surprise:** on a FRESH character (never
touched these settings), BOTH "Disable Sound Effects" and "Disable
Ambient Sound" checkboxes are CHECKED by default — i.e., sound starts
MUTED out of the box. This is retail's own byte-verified 2013 EoR
default (`gmConfigUI::InitOptions`'s `SetDefaultValue(1, ...)` on both
trios), reproduced faithfully — **not a bug**, however counterintuitive.
Uncheck both to hear anything.
7. **Drag the third slider / check its toggle ("Disable Interface Sound",
third trio).** Expect NO audible change either way — this is retail's
OWN dead knob (register row AP-174/AP-199): retail registers this
preference and then never reads it; UI/interface sounds are always
scaled by the Sound Effects slider instead. Confirm the row is
clickable/draggable and does not crash; do not expect it to do anything.
### Live rows — display
8. **Open the Resolution menu (Graphics Options, first row) and pick a
different resolution.** The window should resize IMMEDIATELY, live, no
restart needed.
9. **Toggle "Full Screen".** The window should switch between windowed and
fullscreen IMMEDIATELY, live.
10. **Toggle "Sync To Refresh" (VSync) and drag the "Field of View"
slider.** Both persist to `settings.json`, but — matching this
project's pre-existing (not new) behaviour for these two fields —
neither re-applies until the NEXT LAUNCH. Do not expect an immediate
visual change; do a full relaunch afterward (step 15) to confirm the
new value took effect at startup.
### Store-only rows — no observable effect is the CONTRACTED behaviour
11. **Sound Features menu (Stereo/Mono), Play Sound Only When Active
toggle.** No consumer exists (register row AP-199). Confirm they're
clickable and persist (step 14); expect no audible/behavioural change.
12. **Screen Brightness slider, Automatic Degrades toggle, Graphics
Performance slider, Degrade Distance slider, the four Rendering
Quality menus (Landscape/Environment Texture Detail, Texture
Filtering, Landscape Draw Distance), Building Detail Textures,
Multi-Pass Alpha.** No consumer exists — acdream's Vulkan renderer is
driven by one aggregate quality preset, not these per-feature knobs
(register row AP-198). Confirm every one is clickable/draggable and
persists; expect zero visual change from any of them. **Landscape
Draw Distance specifically may show NO highlighted item** even right
after Defaults — that is retail's own decompiled data being
ambiguous (AP-198's own sub-note), not a rendering bug.
13. **Mouse Look Sensitivity slider, Invert Mouselook Y Axis toggle, Use
Mouse Turning toggle (Input Options), Camera Stiffness/Adjustment
Speed sliders, Align To Slope toggle (Camera Options), Chat Font
Face/Size menus (UI Options).** No consumer exists for any of these
six Camera/Input rows (register row TS-74 — the SAME "no persistent
mouse-turning camera mode" gap the Gameplay tab's macro already
exposed) or the two Chat font rows (register row AP-200 — distinct
fields from the chat panel's own separately-live font-size control,
wherever that is currently exposed). Confirm all eight are
clickable/draggable and persist; expect no visual/audible change.
### Apply/Reset/Defaults
14. **Toggle a mix of row types (a checkbox, a slider, a menu selection),
then click Reset.** Every row you touched reverts to its
last-shown/Applied value — live audio/display rows revert their
ACTUAL effect too (sound un-mutes or re-mutes, resolution changes
back), not just the checkbox art.
15. **Click Defaults.** Every row jumps LIVE to retail's byte-verified
default (Sound/Ambient trios back to muted+100% volume, Resolution to
800×600, Full Screen on, Camera Stiffness to 0.45, etc.) — Apply/Reset
light up afterward if anything actually changed (Defaults never
commits by itself).
16. **Switch to another tab (e.g. Gameplay) without clicking Apply after
an edit.** Uncommitted edits silently revert — switching tabs is a
Reset, not a save.
### Persistence — local-only, survives relaunch
17. **Set a distinctive combination** (e.g. unmute Sound Effects at 40%
volume, pick a non-default Resolution, drag Field of View partway) —
every row here writes to `settings.json` on change, not just on
Apply.
18. **Fully relaunch the client** (close the window, start a new
process — a relog is not required, this is all local-only state).
Reopen the Options panel's Config tab — every row (live AND
store-only) should read back at your set values. The Sync To
Refresh/Field Of View rows deferred to next-launch (step 10) should
now ALSO be visibly applied (window vsync behaviour / camera FOV).
### What to report
- Any section/row/label missing, or a slider showing a low/high range
caption (retail ships none on this tab — see this section's own intro).
- A live row (Sound/Ambient volume+mute, Resolution, Full Screen) that
does not change live, or a store-only row that unexpectedly DOES change
something (a sign its "no consumer" premise is stale).
- Reset/Defaults not behaving as described in 14-15.
- Any row that fails to persist across a full relaunch (step 18),
including the two next-launch-only rows (Sync To Refresh, Field of
View) not taking effect at the NEXT startup.
- Any crash, freeze, or scroll glitch anywhere in the 27-row list.
### Explicitly NOT in scope for this gate
- Any audible/visual change from the store-only rows listed in steps
11-13 — no acdream consumer exists for any of them (register rows
AP-198, AP-199, AP-200, TS-74).
- The camera actually turning from "Use Mouse Turning" (Config tab OR the
Gameplay tab's macro) — no persistent mouse-turning camera mode exists
(TS-74, already covered by OP3's gate).
- Configure Keyboard — OP8.
---
## OP7 — headless `characterOptions`
Unlike OP3-OP6, this is not a graphical-client gate: no window is launched.