using System.Collections.Generic; using AcDream.Core.Chat; using AcDream.UI.Abstractions.Panels.Settings; namespace AcDream.App.UI.Layout; /// /// Pure port of gmConfigUI::SetMouseTurningDefaults @0x0049E8F0 — the /// Gameplay tab's "Use Mouse Turning Settings" button /// (docs/research/2026-08-10-keyboard-config-and-gameplay-tab.md §4.4). /// A one-shot "apply the recommended mouse-turning values" macro over six /// options: five CLIENT-LOCAL preferences /// () plus one server-synced bit /// (PlayerOption.UseMouseTurning, NOT modeled here — the caller /// threads the current/target value in and reads /// back out to decide whether to /// send SetSingleCharacterOption (0x0005)). /// /// /// Byte-verified per-value conditions (research doc §4.4, the MSVC /// fcomp/fnstsw/jnp idiom — the body runs when the /// CURRENT value differs from the macro's target, exact float equality, no /// epsilon): Stiffness → 0.95 iff ≠ 0.95; AdjustmentSpeed → 50.0 iff /// ≠ 50.0; MouseLookSensitivity → 0.7 iff ≠ 0.7; AlignToSlope /// → off iff currently on; InvertMouseLookYAxis → on iff currently off; /// UseMouseTurning → on iff currently off. Retail's directional bool /// conditions (== 1 / == 0) are behaviourally identical to a /// plain inequality against the fixed target for a two-state bool, so this /// port uses != uniformly. /// /// /// /// Retail emits one retail-verbatim chat line PER CHANGED value (never for /// an already-at-target value) and finishes with /// SaveCurrentValues() (the caller's job — the macro's target page is /// the Config tab's own , which this class does not /// know about). /// /// public static class MouseTurningSettingsMacro { public readonly record struct Result( CameraTurningSettings Updated, bool UseMouseTurningChanged, IReadOnlyList ChatLines); /// /// Computes the macro's outcome. Pure — no I/O, no side effects; the /// caller applies (persist + live apply), /// sends the wire bit if , /// and emits through the interface-text /// seam, in that order (retail's own call order at /// 0x0049E924-0x0049EB57). /// public static Result Compute(CameraTurningSettings current, bool useMouseTurningCurrent) { CameraTurningSettings target = CameraTurningSettings.MouseTurningTarget; var lines = new List(); 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); } }