fix #389 review round: settings v3 FOV migration + live apply; AD-90
Dual-lens Opus review of 7e0c1303 (reports committed under
docs/research/). The law, gate, and vertical application are CONFIRMED
at instruction-byte level against the PDB-paired acclient.exe (the BN
text FPU-elides this whole area); the fix round addresses the findings:
- Blast MUST-FIX 1: real schema migration instead of a hand-edited dev
file. SettingsStore v2->v3: a pre-v3 display.fieldOfView was the
applied vertical FOV in degrees; v3 means retail's m_fGameFOV.
LoadDisplay migrates on read - the untouched old default 60 maps to
the retail default 90; a deliberate other value preserves its visible
16:9 framing (x (16/9 - 0.1)), clamped to the registered [10,160];
the next save stamps v3 and migration never reruns. The dev
settings.json hand-edit was reverted so the migration owns it.
- Blast MUST-FIX 2 / mechanism M2: the Field of View now applies LIVE on
Save (retail: Render::GRPCallback_OnRenderPreferenceChanged @0x0054d999
-> SmartBox::SetDefaultFov). RuntimeSettingsTargets gains the camera
graph and applies through ApplyDisplayWindowState - the update-phase
seam, deliberately NOT the render-phase preview path (the review's
WATCH-3 cull-vs-raster landmine).
- Mechanism M1 -> register row AD-90: retail's divisor aspect runs
through the Render.AspectRatio preference (ComputeAspectForViewport
@0x0054f150, (w/h) x pref x 0.75) - exactly raw w/h at the registered
default, which is what acdream assumes; retail's NaN-through-the-gate
quirk (M3) is folded into the same row as deliberately not reproduced.
- Docs: RetailFieldOfView now cites the decisive vertical proof
(D3DXMatrixPerspectiveFovLH fovy slot @0x0059ab71), the unconditional
SmartBox::RenderNormalMode site, and M4's exact horizontal numbers
(89.0/83.9/80.6 deg); the Config FOV row comment updated to LIVE.
- Blast WATCH 4 disposition: the 15 replay-harness PI/3 constants stay -
they are CAPTURE-TIME camera parameters for recorded fixtures, not
production framing; changing them would invalidate the replays.
Tests: +6 SettingsStore migration facts, +1 live-apply fact.
App suite 4,962/3 skips; UI.Abstractions 922.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
6b844c142f
commit
d13d63d0a5
11 changed files with 1032 additions and 21 deletions
|
|
@ -20,10 +20,14 @@ public readonly record struct UiWindowPosition(float X, float Y);
|
|||
/// <see cref="Input.KeyBindings.LoadOrDefault"/> path.
|
||||
///
|
||||
/// <para>
|
||||
/// Schema (current version 2):
|
||||
/// Schema (current version 3 — v3 changed the MEANING of
|
||||
/// <c>display.fieldOfView</c>: it was the applied vertical FOV in degrees,
|
||||
/// it is now retail's <c>m_fGameFOV</c> in degrees (#389, the SmartboxFOV
|
||||
/// port). <see cref="LoadDisplay"/> migrates pre-v3 values on read — see
|
||||
/// <see cref="MigrateLegacyVerticalFovDegrees"/>):
|
||||
/// <code>
|
||||
/// {
|
||||
/// "version": 2,
|
||||
/// "version": 3,
|
||||
/// "display": { "resolution": "1920x1080", "fullscreen": false, ... }
|
||||
/// "windowLayouts": { "Character": { "1920x1080": { "chat": { ... } } } }
|
||||
/// }
|
||||
|
|
@ -39,7 +43,7 @@ public readonly record struct UiWindowPosition(float X, float Y);
|
|||
/// </summary>
|
||||
public sealed class SettingsStore
|
||||
{
|
||||
private const int CurrentSchemaVersion = 2;
|
||||
private const int CurrentSchemaVersion = 3;
|
||||
private readonly string _path;
|
||||
|
||||
public SettingsStore(string path)
|
||||
|
|
@ -66,11 +70,18 @@ public sealed class SettingsStore
|
|||
return DisplaySettings.Default;
|
||||
|
||||
var d = DisplaySettings.Default;
|
||||
// #389 (schema v3): a pre-v3 file's stored fieldOfView was the
|
||||
// applied vertical FOV; the same number now means retail's
|
||||
// gameFOV. Migrate on read — an absent key needs none (it falls
|
||||
// back to the already-v3 default).
|
||||
float fieldOfView = ReadFloat(disp, "fieldOfView", d.FieldOfView);
|
||||
if (ReadSchemaVersion(root) < 3 && disp.TryGetProperty("fieldOfView", out _))
|
||||
fieldOfView = MigrateLegacyVerticalFovDegrees(fieldOfView);
|
||||
return new DisplaySettings(
|
||||
Resolution: ReadString (disp, "resolution", d.Resolution),
|
||||
Fullscreen: ReadBool (disp, "fullscreen", d.Fullscreen),
|
||||
VSync: ReadBool (disp, "vsync", d.VSync),
|
||||
FieldOfView: ReadFloat (disp, "fieldOfView", d.FieldOfView),
|
||||
FieldOfView: fieldOfView,
|
||||
Gamma: ReadFloat (disp, "gamma", d.Gamma),
|
||||
ShowFps: ReadBool (disp, "showFps", d.ShowFps),
|
||||
Quality: ReadQuality (disp, "quality", d.Quality),
|
||||
|
|
@ -94,6 +105,38 @@ public sealed class SettingsStore
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>The file's top-level schema version; a file predating the
|
||||
/// version key reads as 1 (the same convention the v1 radar-layout
|
||||
/// migration below already uses).</summary>
|
||||
private static int ReadSchemaVersion(JsonElement root) =>
|
||||
root.TryGetProperty("version", out var v) && v.ValueKind == JsonValueKind.Number
|
||||
? v.GetInt32()
|
||||
: 1;
|
||||
|
||||
/// <summary>
|
||||
/// #389 blast-review MUST-FIX 1 — the v3 fieldOfView migration. The
|
||||
/// pre-v3 number was the applied VERTICAL FOV in degrees (written
|
||||
/// straight onto the cameras); the v3 number is retail's
|
||||
/// <c>m_fGameFOV</c> in degrees, run through the smartbox law. Two
|
||||
/// cases:
|
||||
/// - exactly the old default 60 → the new default 90: 60 was acdream's
|
||||
/// own invented constant, not a user preference, and mapping it to
|
||||
/// retail's registered default gives every untouched file the retail
|
||||
/// experience;
|
||||
/// - anything else was a DELIBERATE choice → preserve the user's
|
||||
/// visible framing at the era's default 16:9 aspect:
|
||||
/// gameFOV = vFOV × (16/9 − 0.1) (the smartbox law inverted at that
|
||||
/// aspect), clamped into the slider's registered [10,160].
|
||||
/// Runs on READ for any pre-v3 file; the next save stamps version 3 and
|
||||
/// the value stops migrating.
|
||||
/// </summary>
|
||||
internal static float MigrateLegacyVerticalFovDegrees(float legacyVerticalFovDegrees)
|
||||
{
|
||||
if (legacyVerticalFovDegrees == 60f)
|
||||
return 90f;
|
||||
return Math.Clamp(legacyVerticalFovDegrees * (16f / 9f - 0.1f), 10f, 160f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save Display settings, preserving any other top-level keys the file
|
||||
/// already contains (e.g. an <c>audio</c> section written by a newer
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue