Commit graph

6 commits

Author SHA1 Message Date
Erik
afa4200107 feat(A.5 T22.5): QualityPreset schema + tests (commit 1/2)
Add QualityPreset enum + QualitySettings readonly record struct with
From(preset) table and WithEnvOverrides() env-var override layer.
Four presets (Low/Medium/High/Ultra) drive NearRadius, FarRadius,
MsaaSamples, AnisotropicLevel, AlphaToCoverage, MaxCompletionsPerFrame.
Env vars (ACDREAM_NEAR_RADIUS, ACDREAM_FAR_RADIUS, ACDREAM_MSAA_SAMPLES,
ACDREAM_ANISOTROPIC, ACDREAM_A2C, ACDREAM_MAX_COMPLETIONS_PER_FRAME)
override individual preset fields for dev spot-testing.

DisplaySettings gains a Quality: QualityPreset field (default High);
SettingsStore persists/loads it under display."quality" as an enum
name string with Enum.TryParse fallback. 12 new QualityPresetTests
cover the preset table (radii, msaa, aniso, a2c, completions) and all
six env-var override paths. 415 UI.Abstractions tests passing.

Wiring into GameWindow / WbDrawDispatcher / TerrainAtlas follows in
commit 2 of this task.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 08:37:17 +02:00
Erik
73749d176a feat(ui): Character tab — per-toon settings; Phase L.0 complete
Phase L.0 (final) — last tab on the Settings shell. Per-toon
preferences keyed by toon name in settings.json under
character[<toonName>]. With this commit the L.0 build order
finishes and every approved tab is implemented.

CharacterSettings record (4 fields):
 · DefaultChatChannel (string — Local / Allegiance / Fellowship / etc)
 · AutoAttack (bool — continue swinging until target dies)
 · ConfirmSalvage (bool — prompt before salvaging valuable items)
 · ShowPickupMessages (bool — pickup lines in chat)

AvailableChannels static list exposes the 7 retail-routing targets
for the dropdown.

SettingsStore grows LoadCharacter(toonKey) / SaveCharacter(toonKey)
using JsonNode/JsonObject for the nested-toon write — the existing
SaveSection raw-text-preservation pattern handles top-level keys
but doesn't fit the nested per-toon mutation. The character map
preserves every other toon's settings on save, and other top-level
sections (display / audio / gameplay / chat) are preserved too.

SettingsVM grows the parallel character state machine. The host
owns the toonKey (currently hard-coded to "default" in GameWindow
because we don't have a current-character source plumbed yet) —
the VM just edits whatever bag the host loaded.

SettingsPanel.RenderCharacterTab replaces the L.0-shell placeholder
— a Combo for default chat channel + 3 Checkboxes for
AutoAttack / ConfirmSalvage / ShowPickupMessages. The
RenderPlaceholder helper is now removed (no callers); the old
"Placeholder_tabs_render_coming_soon_text_when_active" test is
replaced by an "all six tabs are implemented" guard test that
fails if any future commit adds a placeholder back.

GameWindow loads/saves character settings under toonKey "default"
with a TODO comment to swap in the real toon name once
CharacterList plumbing exposes a currentCharacter source.

18 new tests:
 · CharacterSettings record (4) — defaults pinned, AvailableChannels
   list shape, value equality, with-expressions
 · SettingsStore character (6) — missing-file / toon-not-in-file →
   defaults, round-trip, multi-toon preservation, preserves other
   top-level sections, all five sections coexist
 · SettingsVM character (5) — initial draft, SetCharacter marks
   dirty, Save invokes callback, Cancel reverts, ResetAllToDefaults
   covers
 · SettingsPanel character tab (3 net, after removing the
   placeholder test) — combo+checkboxes render only when active,
   channel combo uses AvailableChannels, all six tabs are now
   non-placeholder

Phase L.0 final tally:
 · 5 commits on feature/settings-retail (shell + 5 tabs)
 · 6 tabs: Keybinds (Phase K) + Display + Audio + Gameplay + Chat + Character
 · 5 settings sections in settings.json (display/audio/gameplay/chat/character),
   coexisting non-destructively + a sixth file (keybinds.json) on the side.

dotnet build green (0 warnings); dotnet test 1,307 / 1,307 green
(243 Core.Net + 391 UI.Abstractions + 673 Core).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 18:27:07 +02:00
Erik
356b5f219e feat(ui): Chat tab — channel filters + display prefs + font slider
Phase L.0 (cont.) — fourth tab on the Settings shell. Mixes retail's
CharacterOptions2 chat-channel filter bits (Hear*Chat / TimeStamp /
FilterLanguage / AppearOffline) with a font-size slider that has no
retail bitfield equivalent.

ChatSettings record (9 fields):
 · 5 channel filters: HearGeneralChat, HearTradeChat, HearLFGChat,
   HearRoleplayChat, HearSocietyChat
 · 3 display flags: ShowTimestamps, FilterProfanity, AppearOffline
 · 1 visual: FontSize (10..20 pt)

Local-only this phase per the brainstorm — Hear*Chat flags affect
client-side display filtering only; the server still streams every
channel. Server-sync arrives later when the protocol round-trip is
in place.

SettingsStore grows LoadChat / SaveChat using the existing generic
SaveSection helper. All four non-keybind sections (display, audio,
gameplay, chat) now coexist non-destructively in settings.json.

SettingsVM grows the parallel chat state machine. HasUnsavedChanges,
Save, Cancel, ResetAllToDefaults all cover chat. Constructor signature
adds two more params; existing call sites updated.

SettingsPanel.RenderChatTab replaces the L.0-shell placeholder —
8 Checkbox calls grouped under "Channel filters" + "Display"
headers, plus a font-size SliderFloat. The "Coming soon" placeholder
test was retargeted from "Chat" to "Character" since Chat is no
longer a placeholder.

GameWindow wires SettingsStore.LoadChat / SaveChat + a TODO comment
for the future ChatPanel filter integration (read SettingsVM.ChatDraft
when filtering inbound chat lines).

13 new tests:
 · ChatSettings record (3) — defaults pinned, value equality, with-
   expressions
 · SettingsStore chat (3) — missing-file → defaults, round-trip, all
   four sections coexist
 · SettingsVM chat (5) — initial draft, SetChat marks dirty, Save
   invokes callback, Cancel reverts, ResetAllToDefaults covers
 · SettingsPanel chat tab (2) — checkboxes + slider render only when
   active

dotnet build green (0 warnings); dotnet test 1,289 / 1,289 green
(243 Core.Net + 373 UI.Abstractions + 673 Core).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 18:21:14 +02:00
Erik
b7165e5b17 feat(ui): Gameplay tab — 14 retail CharacterOption-derived toggles
Phase L.0 (cont.) — third tab on the Settings shell, in the Easy-wins
build order. Subset of retail's CharacterOption + CharacterOptions2
bitfield flags ported as bools (see acclient.h:3404+ enum). Local-
only this phase per the brainstorm — server sync deferred to a
later phase that will marshal the draft into the retail
CharacterOption packet.

GameplaySettings record exposes 14 named flags grouped by usage:

 · Combat: AutoTarget, AutoRepeatAttack, ToggleRun, AdvancedCombatUI,
   VividTargetingIndicator
 · Display: ShowTooltips, SideBySideVitals, CoordinatesOnRadar,
   SpellDuration, ShowHelm, ShowCloak
 · Interface: AllowGive, LockUI, UseMouseTurning

Retail names + bit values are documented in field-level comments so
the future server-sync phase has a 1:1 mapping. Defaults are
typical-user starting points (NOT bit-exact to retail's
0x50C4A54A / 0x948700 masks); class-level remarks call out that
defaults will be re-anchored to retail values once the wire-format
is the load-bearing source.

SettingsStore grows LoadGameplay / SaveGameplay using the existing
SaveSection generic helper (added in the audio commit). All three
non-keybind sections (display, audio, gameplay) now coexist in
settings.json with non-destructive cross-section saves — verified
by a new "all three sections coexist" round-trip test.

SettingsVM grows the parallel gameplay state machine
(gameplayPersisted / gameplayDraft / SetGameplay / onSaveGameplay).
HasUnsavedChanges, Save, Cancel, ResetAllToDefaults all cover
gameplay too. Constructor signature adds two more params; existing
call sites (App startup + tests) updated.

SettingsPanel.RenderGameplayTab replaces the L.0-shell placeholder —
14 Checkbox calls grouped under three Text+Separator headers, plus
a footer note explaining the local-only-this-phase scope. The
"Coming soon" placeholder test was retargeted from "Gameplay" to
"Chat" since Gameplay is no longer a placeholder.

GameWindow construction site loads gameplay on startup + writes via
the SettingsStore on Save. Server-sync packet wiring is left as a
TODO comment in the onSaveGameplay callback (next phase, after the
protocol round-trip is in place).

14 new tests:
 · GameplaySettings record (3) — defaults pinned, value equality,
   with-expressions
 · SettingsStore gameplay (4) — missing-file → defaults, round-trip,
   partial-file fallback, all-three-sections coexist
 · SettingsVM gameplay (5) — initial draft, SetGameplay marks dirty,
   Save invokes callback, Cancel reverts, ResetAllToDefaults covers
 · SettingsPanel gameplay tab (2) — 8 spot-checked Checkboxes render
   only when active

dotnet build green (0 warnings); dotnet test 1,276 / 1,276 green
(243 Core.Net + 360 UI.Abstractions + 673 Core).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 18:05:07 +02:00
Erik
53b1878c5c feat(ui): Audio tab — live volume sliders driving OpenAL engine
Phase L.0 (cont.) — second tab on the Settings shell, in the Easy-wins
build order. Audio is the live-preview poster child: dragging a slider
is audible immediately, Save persists, Cancel reverts and the engine
catches up on the next frame.

AudioSettings record: Master / Music / Sfx / Ambient (all 0..1 floats).
Defaults match the OpenAlAudioEngine constructor values exactly so a
user who never opens the tab gets identical behaviour to the
pre-Phase-L env-var-only world (Master=1.0, Music=0.7, Sfx=1.0,
Ambient=0.8).

SettingsStore grows LoadAudio / SaveAudio + a generic SaveSection
helper that consolidates the unknown-top-level-key preservation logic.
Display and Audio sections coexist in settings.json:
{ "version": 1, "display": { ... }, "audio": { ... } }
Saving one section preserves the other on disk; a future Gameplay /
Chat / Character section drops in the same way without touching
existing data.

SettingsVM gains a parallel audio state machine (audioPersisted /
audioDraft / SetAudio / onSaveAudio callback). HasUnsavedChanges
covers all three buckets now (keybinds + display + audio); Save /
Cancel / ResetAll are atomic across all of them.

GameWindow wiring is the live-preview mechanism — every render frame
pushes the VM's AudioDraft into _audioEngine.MasterVolume etc. Cheap
(four float assignments) and unconditional. SetListener still applies
MasterVolume each frame too via the existing Phase E.2 code path, so
listener gain stays in sync. Persisted audio is applied to the engine
ONCE at startup before the first frame so the user's saved values
take effect before any sound plays — startup-time apply happens during
the same SettingsVM construction site that does the LoadDisplay +
LoadAudio.

SettingsPanel.RenderAudioTab replaces the L.0-shell placeholder — four
SliderFloat calls clamped to [0, 1], plus a footer note explaining the
live-preview UX. The "Coming soon" placeholder test was retargeted
from "Audio" to "Gameplay" since Audio is no longer a placeholder.

16 new tests:
 · AudioSettings record (3) — defaults pin engine constants, value
   equality, with-expressions
 · SettingsStore audio round-trip (5) — missing-file → defaults,
   round-trip all fields, partial-file per-field fallback, save-audio-
   preserves-display, save-display-preserves-audio
 · SettingsVM audio state (5) — initial draft tracks persisted,
   SetAudio marks dirty, Save invokes audio callback, Cancel reverts,
   ResetAllToDefaults covers audio
 · SettingsPanel audio tab (3) — four sliders render only when active,
   no SliderFloat emitted on inactive tabs, slider range is [0, 1]

dotnet build green (0 warnings); dotnet test 1,262 / 1,262 green
(243 Core.Net + 346 UI.Abstractions + 673 Core).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 17:57:00 +02:00
Erik
382f0ad3fa feat(ui): Display tab + settings.json persistence — first non-keybind tab lands
Phase L.0 (cont.) — first concrete tab on the new Settings shell, in
the Easy-wins build order agreed in the brainstorm
(Display → Audio → Gameplay → Chat → Character).

DisplaySettings (immutable record): Resolution / Fullscreen / VSync /
FieldOfView (30-120°) / Gamma (0.5-2.0) / ShowFps. Six common 16:9
resolutions in the dropdown. Defaults: 1920×1080, windowed, vsync on,
75° FOV, gamma 1.0, FPS off — matches the brainstorm UX agreement.

SettingsStore: JSON persistence at %LOCALAPPDATA%\acdream\settings.json
(coexists with keybinds.json — own load/save path stays put, no
migration needed). LoadDisplay falls back per-field when keys are
missing (partial-file tolerant) and falls back to defaults when the
file is corrupt or the JSON is unparseable. SaveDisplay round-trips
preserved — unknown top-level keys (e.g. an `audio` section written
by a future client) are kept on save so older builds don't silently
drop newer-tab data.

SettingsVM gains a parallel display-state machine: persistedDisplay +
draftDisplay, SetDisplay mutator, HasUnsavedChanges checks both
keybinds and display deltas, Save/Cancel/ResetAll cover both
atomically from the user's POV (one Save commits everything, one
Cancel reverts everything). Constructor signature extends with two
new params; existing keybinds-only callers updated.

SettingsPanel.RenderDisplayTab replaces the L.0-shell placeholder —
Combo for resolution, Checkboxes for fullscreen/vsync/show-fps,
SliderFloat for FOV + gamma. Live-preview note in the panel body
matches the agreed UX: FOV + gamma update visibly while the user
drags; resolution / fullscreen / vsync apply on Save (live preview
would be too jarring).

GameWindow wires SettingsStore into the existing SettingsVM construct
site — load on startup, save on each tab Save. Errors print to
console and don't crash the panel.

19 new tests:
 · DisplaySettings record (4) — defaults pinned, value equality, with-
   expressions, AvailableResolutions sorted ascending
 · SettingsStore (6) — round trip, missing-file → defaults, corrupt-
   file → defaults, partial-file → per-field fallback, unknown-key
   preservation, DefaultPath shape
 · SettingsVM display (6) — initial draft tracks persisted, SetDisplay
   marks dirty, Save invokes display callback, Cancel reverts,
   ResetAllToDefaults covers display, Save-then-Cancel is no-op
 · SettingsPanel display tab (3) — widgets render only when active,
   resolution combo uses AvailableResolutions, no Combo emitted on
   inactive tabs

dotnet build green (0 warnings); dotnet test 1,246 / 1,246 green
(243 Core.Net + 330 UI.Abstractions + 673 Core).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 17:46:31 +02:00