fix(ui,runtime): OP4 review fixes — live re-seed, enable-gating, Combat panel re-point, universal timestamps

Both OP4 reviews converged on one headline bug (Character-tab rows never
re-read live server truth after their pre-login constructor-word seed) plus
overlapping MUST-FIXes. All ten converged/consolidated findings land here:

MUST-FIX:
- BoolOptionRow.SaveCurrentValue now re-reads its live binding (retail's
  GetValue()-into-SaveCurrentValue) on every OnShown — panel open, tab
  switch in, initial activation — instead of trusting the pre-login
  constructor word it was built with. Reset/tab-switch can now only
  restore values that were actually live at the last show. LockUI's
  host.Root.UiLocked one-shot mount seed now also converges on every
  PlayerDescription via the existing OnCharacterOptionsChanged hook.
- Apply/Reset are wired to OptionPage.OnOptionChanged in production
  (Ghosted when nothing changed, Normal when dirty, run once at bind so
  both start disabled per retail's PostInit); Defaults stays ungated.
- The Combat panel's three LEDs (Repeat Attacks/Auto Target/Keep in View)
  now read/write the same RuntimeCharacterOptionsState seam the Character
  tab uses instead of a disconnected client-local GameplaySettings copy —
  closes the "two writable copies" divergence. The three now-orphaned
  GameplaySettings fields and RuntimeSettingsController's mirror
  properties/SetCombatGameplay are deleted outright; the headless host's
  hardcoded AutoRepeatAttack/AutoTarget now read the live option bit.
- RuntimeSettingsController.SetUiLocked's convergence guard now compares
  against the last value actually applied to the runtime target instead
  of the persisted GameplaySettings.LockUI snapshot, which could already
  match a server-derived request without ever having been pushed.

SHOULD-FIX:
- DisplayTimeStamps now prefixes every chat producer (ChatLog.Append is
  the one seam all of them funnel through), not just AddText's own
  callers — heard speech, emotes, Turbine channels, and combat text were
  previously missed. The prefix format escapes its colons and forces
  InvariantCulture instead of the culture-dependent TimeSeparator
  placeholder.
- sky.frag now honors uFogParams.w (fog mode) like the mesh/terrain
  shaders, so Disable Distance Fog stops the sky dome's horizon band from
  blending toward fog color too.
- Corrected the "byte-verified" overclaim on the timestamp format string
  doc comment (BN-sourced, wire doc U6) and the AP-194 anchor-column
  class-name typo; the RunAsDefaultMovement doc comments now cite retail's
  actual acclient.h enumerator name.
- Added: DispatcherMovementInputSource's option x modifier truth table
  (incl. || AutoRunActive with the option off), the per-page Apply/Reset
  enable-gate tests, a real checkbox.OnClick/ToggleBehavior-driven click
  test, and hash-pins for the six header string keys.
- Gate script step 8 corrected for the logout-flush false-failure
  (closing the panel before relogging is load-bearing); a new step
  documents the enable-gate sequence and the Combat-panel/Character-tab
  cross-check.

Register: AP-196 (the Group-C default-source change + GameplaySettings
retirement) and AP-197 (the ignored per-character timestamp format
override) filed in this commit.

Full Release suite: 13,044 passed / 4 skipped / 0 failed (was 13,008/4/0;
net +36 tests from new coverage and legitimate assertion updates from the
GameplaySettings retirement).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-11 05:30:26 +02:00
parent 85afaae5fd
commit bc43fb1d1d
36 changed files with 923 additions and 220 deletions

View file

@ -24,9 +24,16 @@ namespace AcDream.UI.Abstractions.Panels.Settings;
/// </summary>
public sealed record GameplaySettings(
// CharacterOption (32-bit) subset — most-used gameplay toggles.
bool AutoTarget, // 0x2000 — combat: auto-acquire target on attack
bool AutoRepeatAttack, // 0x2 — combat: keep attacking after first hit
bool ViewCombatTarget, // 0x80 — keep the current combat target in view
//
// OP4 review-fix round (2026-08-11, MUST-FIX 3 / blast M2): AutoTarget,
// AutoRepeatAttack, and ViewCombatTarget were REMOVED from this record
// — the Combat panel's three LEDs now read/write the SAME canonical
// server-bit seam (RuntimeCharacterOptionsState via
// CharacterOptionCombatSettingsSource) the Character tab's rows for
// these same retail PlayerOptions already used, closing the
// "two writable copies" divergence (register row AP-196). This record
// remains the client-local persistence/draft store for every OTHER
// gameplay preference that has no such server-authoritative seam.
bool ToggleRun, // 0x400 — run-mode is tap-once vs hold-to-run
bool AdvancedCombatUI, // 0x1000 — show extra combat tooltips/panels
bool ShowTooltips, // 0x100 — show item tooltips on hover
@ -46,9 +53,6 @@ public sealed record GameplaySettings(
/// to retail's <c>Default_CharacterOption = 0x50C4A54A</c> +
/// <c>Default_CharacterOptions2 = 0x948700</c> — see class remarks.</summary>
public static GameplaySettings Default { get; } = new(
AutoTarget: true,
AutoRepeatAttack: true,
ViewCombatTarget: true,
ToggleRun: true,
AdvancedCombatUI: false,
ShowTooltips: true,

View file

@ -299,17 +299,15 @@ public sealed class SettingsPanel : IPanel
renderer.Text("Combat");
renderer.Separator();
bool autoTarget = g.AutoTarget;
if (renderer.Checkbox("Auto-target on attack", ref autoTarget))
_vm.SetGameplay(g with { AutoTarget = autoTarget });
bool autoRepeat = g.AutoRepeatAttack;
if (renderer.Checkbox("Auto-repeat attacks", ref autoRepeat))
_vm.SetGameplay(g with { AutoRepeatAttack = autoRepeat });
bool viewCombatTarget = g.ViewCombatTarget;
if (renderer.Checkbox("Keep combat target in view", ref viewCombatTarget))
_vm.SetGameplay(g with { ViewCombatTarget = viewCombatTarget });
// OP4 review-fix round (2026-08-11, MUST-FIX 3 / blast M2):
// Auto-target/Auto-repeat/Keep-in-view were removed from
// GameplaySettings — they now read/write the canonical server bit
// (RuntimeCharacterOptionsState) through CombatUiController, not
// this client-local draft. This panel has no production
// construction site (D.2b's retained UiHost/UiRoot stack is the
// one presentation surface — see CLAUDE.md's UI strategy section);
// the three checkboxes are simply retired rather than re-pointed
// to a seam this dat-free ImGui-shaped panel has no way to reach.
bool toggleRun = g.ToggleRun;
if (renderer.Checkbox("Run mode is toggle (vs hold)", ref toggleRun))

View file

@ -144,9 +144,6 @@ public sealed class SettingsStore
var d = GameplaySettings.Default;
return new GameplaySettings(
AutoTarget: ReadBool(gp, "autoTarget", d.AutoTarget),
AutoRepeatAttack: ReadBool(gp, "autoRepeatAttack", d.AutoRepeatAttack),
ViewCombatTarget: ReadBool(gp, "viewCombatTarget", d.ViewCombatTarget),
ToggleRun: ReadBool(gp, "toggleRun", d.ToggleRun),
AdvancedCombatUI: ReadBool(gp, "advancedCombatUI", d.AdvancedCombatUI),
ShowTooltips: ReadBool(gp, "showTooltips", d.ShowTooltips),
@ -603,9 +600,6 @@ public sealed class SettingsStore
["advancedCombatUI"] = g.AdvancedCombatUI,
["acceptLootPermits"] = g.AcceptLootPermits,
["allowGive"] = g.AllowGive,
["autoRepeatAttack"] = g.AutoRepeatAttack,
["autoTarget"] = g.AutoTarget,
["viewCombatTarget"] = g.ViewCombatTarget,
["coordinatesOnRadar"] = g.CoordinatesOnRadar,
["lockUI"] = g.LockUI,
["showCloak"] = g.ShowCloak,