Mounts retail's Options panel (LayoutDesc 0x2100002B resolved through host 0x2100006E slot 0x1000018D, gmPanelUI key 10) via the same catalog-import pattern CharacterController already validates, registered through RetailPanelUiController so it shares retail's "one active gmPanelUI child" mutual exclusion with every other sibling panel for free. F11 and the toolbar's options button (0x1000019B, already authoring panel id 10) both now open it; the close button fires the same ToggleOptionsPanel action. OptionPageModel (OptionPage/BoolOptionRow) ports retail's exact Apply/Reset/Defaults/visibility semantics from UIOption_Checkbox/PlayerOptionPage — LED clicks apply live immediately, Apply commits every row unconditionally + flushes the batched blob, Reset reverts only Changed rows, Defaults restores without committing, and tab-switch/window-hide revert uncommitted edits. Wired for all four tabs; this slice registers real rows on none of them (Gameplay authentically has none — a pure button list). UiTabPanel gains an ActivePageChanged event so the page model can hook every tab transition, including the initial default-tab activation. The seven Gameplay-tab buttons: Exit Game reuses the existing graceful window-close path; Exit to Character Selection gets retail's confirmation dialog and byte-verified mid-air refusal but still behaves as Exit Game (AD-74 — no pre-world character-select flow exists); Configure Keyboard and In-Game Help Files are inert this slice (AD-76 for Help — the plugin retail depends on doesn't exist); Urgent Assistance/Report Abuse short-circuit to their own byte-verified failure text through the interface-text seam instead of ShellExecute against a dead URL (AD-75); Use Mouse Turning Settings runs the pure MouseTurningSettingsMacro port, persisting five new CameraTurningSettings preferences and sending PlayerOption.UseMouseTurning — TS-74 records that acdream has no persistent mouse-turning camera mode for the bit to drive yet. Full Release suite: 12,918 passed / 4 skipped / 0 failed (baseline 12,871/4/0 — only new tests added). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
106 lines
4.5 KiB
C#
106 lines
4.5 KiB
C#
using System.Collections.Generic;
|
|
using AcDream.Core.Chat;
|
|
using AcDream.UI.Abstractions.Panels.Settings;
|
|
|
|
namespace AcDream.App.UI.Layout;
|
|
|
|
/// <summary>
|
|
/// Pure port of <c>gmConfigUI::SetMouseTurningDefaults @0x0049E8F0</c> — the
|
|
/// Gameplay tab's "Use Mouse Turning Settings" button
|
|
/// (<c>docs/research/2026-08-10-keyboard-config-and-gameplay-tab.md</c> §4.4).
|
|
/// A one-shot "apply the recommended mouse-turning values" macro over six
|
|
/// options: five CLIENT-LOCAL preferences
|
|
/// (<see cref="CameraTurningSettings"/>) plus one server-synced bit
|
|
/// (<c>PlayerOption.UseMouseTurning</c>, NOT modeled here — the caller
|
|
/// threads the current/target value in and reads
|
|
/// <see cref="Result.UseMouseTurningChanged"/> back out to decide whether to
|
|
/// send <c>SetSingleCharacterOption (0x0005)</c>).
|
|
///
|
|
/// <para>
|
|
/// Byte-verified per-value conditions (research doc §4.4, the MSVC
|
|
/// <c>fcomp</c>/<c>fnstsw</c>/<c>jnp</c> idiom — the body runs when the
|
|
/// CURRENT value differs from the macro's target, exact float equality, no
|
|
/// epsilon): Stiffness → 0.95 iff <c>≠ 0.95</c>; AdjustmentSpeed → 50.0 iff
|
|
/// <c>≠ 50.0</c>; MouseLookSensitivity → 0.7 iff <c>≠ 0.7</c>; AlignToSlope
|
|
/// → off iff currently on; InvertMouseLookYAxis → on iff currently off;
|
|
/// UseMouseTurning → on iff currently off. Retail's directional bool
|
|
/// conditions (<c>== 1</c> / <c>== 0</c>) are behaviourally identical to a
|
|
/// plain inequality against the fixed target for a two-state bool, so this
|
|
/// port uses <c>!=</c> uniformly.
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// Retail emits one retail-verbatim chat line PER CHANGED value (never for
|
|
/// an already-at-target value) and finishes with
|
|
/// <c>SaveCurrentValues()</c> (the caller's job — the macro's target page is
|
|
/// the Config tab's own <see cref="OptionPage"/>, which this class does not
|
|
/// know about).
|
|
/// </para>
|
|
/// </summary>
|
|
public static class MouseTurningSettingsMacro
|
|
{
|
|
public readonly record struct Result(
|
|
CameraTurningSettings Updated,
|
|
bool UseMouseTurningChanged,
|
|
IReadOnlyList<string> ChatLines);
|
|
|
|
/// <summary>
|
|
/// Computes the macro's outcome. Pure — no I/O, no side effects; the
|
|
/// caller applies <see cref="Result.Updated"/> (persist + live apply),
|
|
/// sends the wire bit if <see cref="Result.UseMouseTurningChanged"/>,
|
|
/// and emits <see cref="Result.ChatLines"/> through the interface-text
|
|
/// seam, in that order (retail's own call order at
|
|
/// <c>0x0049E924</c>-<c>0x0049EB57</c>).
|
|
/// </summary>
|
|
public static Result Compute(CameraTurningSettings current, bool useMouseTurningCurrent)
|
|
{
|
|
CameraTurningSettings target = CameraTurningSettings.MouseTurningTarget;
|
|
var lines = new List<string>();
|
|
|
|
float stiffness = current.Stiffness;
|
|
if (stiffness != target.Stiffness)
|
|
{
|
|
lines.Add(OptionsPanelText.CameraStiffnessChanged(stiffness, target.Stiffness));
|
|
stiffness = target.Stiffness;
|
|
}
|
|
|
|
float adjustmentSpeed = current.AdjustmentSpeed;
|
|
if (adjustmentSpeed != target.AdjustmentSpeed)
|
|
{
|
|
lines.Add(OptionsPanelText.CameraAdjustmentChanged(adjustmentSpeed, target.AdjustmentSpeed));
|
|
adjustmentSpeed = target.AdjustmentSpeed;
|
|
}
|
|
|
|
float sensitivity = current.MouseLookSensitivity;
|
|
if (sensitivity != target.MouseLookSensitivity)
|
|
{
|
|
lines.Add(OptionsPanelText.MouseSensitivityChanged(sensitivity, target.MouseLookSensitivity));
|
|
sensitivity = target.MouseLookSensitivity;
|
|
}
|
|
|
|
bool alignToSlope = current.AlignToSlope;
|
|
if (alignToSlope != target.AlignToSlope)
|
|
{
|
|
lines.Add(OptionsPanelText.AlignToSlopeChanged);
|
|
alignToSlope = target.AlignToSlope;
|
|
}
|
|
|
|
bool invertY = current.InvertMouseLookYAxis;
|
|
if (invertY != target.InvertMouseLookYAxis)
|
|
{
|
|
lines.Add(OptionsPanelText.InvertMouseLookAxesChanged);
|
|
invertY = target.InvertMouseLookYAxis;
|
|
}
|
|
|
|
// UseMouseTurning's target is always ON (true) — retail's
|
|
// `== 0` condition ("currently off").
|
|
bool useMouseTurningChanged = useMouseTurningCurrent != true;
|
|
if (useMouseTurningChanged)
|
|
lines.Add(OptionsPanelText.TurnToFaceCameraChanged);
|
|
|
|
return new Result(
|
|
new CameraTurningSettings(stiffness, adjustmentSpeed, sensitivity, alignToSlope, invertY),
|
|
useMouseTurningChanged,
|
|
lines);
|
|
}
|
|
}
|