diff --git a/src/AcDream.App/Rendering/CameraController.cs b/src/AcDream.App/Rendering/CameraController.cs
index 1673ba0..c855a9a 100644
--- a/src/AcDream.App/Rendering/CameraController.cs
+++ b/src/AcDream.App/Rendering/CameraController.cs
@@ -11,11 +11,11 @@ public sealed class CameraController
public RetailChaseCamera? RetailChase { get; private set; }
///
- /// The renderer-facing active camera. In chase mode, returns
- /// when
- /// is true,
- /// otherwise . In fly mode returns
- /// ; default is .
+ /// The renderer-facing active camera. Both the legacy and retail
+ /// chase cameras are held simultaneously so that flipping
+ /// takes effect
+ /// on the very next access to this property — no re-entry required,
+ /// no notification mechanism, no stale state.
///
public ICamera Active
{
@@ -53,9 +53,8 @@ public sealed class CameraController
}
///
- /// Enter chase mode with both candidate cameras. Both are held;
- /// picks based on
- /// .
+ /// Store both cameras simultaneously; picks
+ /// between them per-read via the flag — no re-entry needed on flip.
///
public void EnterChaseMode(ChaseCamera legacy, RetailChaseCamera retail)
{
diff --git a/src/AcDream.UI.Abstractions/Input/InputAction.cs b/src/AcDream.UI.Abstractions/Input/InputAction.cs
index a922353..9beb39f 100644
--- a/src/AcDream.UI.Abstractions/Input/InputAction.cs
+++ b/src/AcDream.UI.Abstractions/Input/InputAction.cs
@@ -262,4 +262,14 @@ public enum InputAction
/// Fly-camera descend (Ctrl) — only meaningful while fly camera
/// is active. K.1b binds it to ControlLeft; K.1c may rebind.
AcdreamFlyDown,
+
+ // ── AcdreamCameraCommands ─────────────────────────────
+ /// Camera zoom in (held key, integrates Distance−= adjSpeed·dt). Default unbound.
+ CameraZoomIn,
+ /// Camera zoom out (held key, integrates Distance+= adjSpeed·dt). Default unbound.
+ CameraZoomOut,
+ /// Camera raise (held key, integrates Pitch+= adjSpeed·dt·0.02). Default unbound.
+ CameraRaise,
+ /// Camera lower (held key, integrates Pitch−= adjSpeed·dt·0.02). Default unbound.
+ CameraLower,
}
diff --git a/src/AcDream.UI.Abstractions/Panels/Debug/DebugVM.cs b/src/AcDream.UI.Abstractions/Panels/Debug/DebugVM.cs
index ef99a25..baf1055 100644
--- a/src/AcDream.UI.Abstractions/Panels/Debug/DebugVM.cs
+++ b/src/AcDream.UI.Abstractions/Panels/Debug/DebugVM.cs
@@ -1,6 +1,7 @@
using System.Numerics;
using AcDream.Core.Combat;
using AcDream.Core.Physics;
+using AcDream.Core.Rendering;
namespace AcDream.UI.Abstractions.Panels.Debug;
@@ -290,6 +291,50 @@ public sealed class DebugVM
set => PhysicsDiagnostics.ProbeAutoWalkEnabled = value;
}
+ // ── Chase camera tunables (forward to CameraDiagnostics) ──────────
+
+ /// Runtime mirror of .
+ public bool UseRetailChaseCamera
+ {
+ get => CameraDiagnostics.UseRetailChaseCamera;
+ set => CameraDiagnostics.UseRetailChaseCamera = value;
+ }
+
+ /// Runtime mirror of .
+ public bool CameraAlignToSlope
+ {
+ get => CameraDiagnostics.AlignToSlope;
+ set => CameraDiagnostics.AlignToSlope = value;
+ }
+
+ /// Runtime mirror of .
+ public float CameraTranslationStiffness
+ {
+ get => CameraDiagnostics.TranslationStiffness;
+ set => CameraDiagnostics.TranslationStiffness = value;
+ }
+
+ /// Runtime mirror of .
+ public float CameraRotationStiffness
+ {
+ get => CameraDiagnostics.RotationStiffness;
+ set => CameraDiagnostics.RotationStiffness = value;
+ }
+
+ /// Runtime mirror of .
+ public float CameraMouseLowPassWindowSec
+ {
+ get => CameraDiagnostics.MouseLowPassWindowSec;
+ set => CameraDiagnostics.MouseLowPassWindowSec = value;
+ }
+
+ /// Runtime mirror of .
+ public float CameraAdjustmentSpeed
+ {
+ get => CameraDiagnostics.CameraAdjustmentSpeed;
+ set => CameraDiagnostics.CameraAdjustmentSpeed = value;
+ }
+
// ── Action hooks invoked by panel buttons ──────────────────────────
///
diff --git a/tests/AcDream.App.Tests/Rendering/CameraControllerTests.cs b/tests/AcDream.App.Tests/Rendering/CameraControllerTests.cs
index d16b879..7bddfe5 100644
--- a/tests/AcDream.App.Tests/Rendering/CameraControllerTests.cs
+++ b/tests/AcDream.App.Tests/Rendering/CameraControllerTests.cs
@@ -1,4 +1,3 @@
-using System.Numerics;
using AcDream.App.Rendering;
using AcDream.Core.Rendering;
using Xunit;
@@ -21,47 +20,76 @@ public class CameraControllerTests
[Fact]
public void ChaseMode_WhenFlagOff_ActiveIsLegacy()
{
- CameraDiagnostics.UseRetailChaseCamera = false;
- var (ctl, legacy, _) = MakeChaseFixture();
- Assert.Same(legacy, ctl.Active);
- Assert.True(ctl.IsChaseMode);
+ bool saved = CameraDiagnostics.UseRetailChaseCamera;
+ try
+ {
+ CameraDiagnostics.UseRetailChaseCamera = false;
+ var (ctl, legacy, _) = MakeChaseFixture();
+ Assert.Same(legacy, ctl.Active);
+ Assert.True(ctl.IsChaseMode);
+ }
+ finally
+ {
+ CameraDiagnostics.UseRetailChaseCamera = saved;
+ }
}
[Fact]
public void ChaseMode_WhenFlagOn_ActiveIsRetail()
{
- CameraDiagnostics.UseRetailChaseCamera = true;
- var (ctl, _, retail) = MakeChaseFixture();
- Assert.Same(retail, ctl.Active);
- Assert.True(ctl.IsChaseMode);
-
- // Reset.
- CameraDiagnostics.UseRetailChaseCamera = false;
+ bool saved = CameraDiagnostics.UseRetailChaseCamera;
+ try
+ {
+ CameraDiagnostics.UseRetailChaseCamera = true;
+ var (ctl, _, retail) = MakeChaseFixture();
+ Assert.Same(retail, ctl.Active);
+ Assert.True(ctl.IsChaseMode);
+ }
+ finally
+ {
+ CameraDiagnostics.UseRetailChaseCamera = saved;
+ }
}
[Fact]
public void ChaseMode_FlagFlipped_ActiveSwaps()
{
- CameraDiagnostics.UseRetailChaseCamera = false;
- var (ctl, legacy, retail) = MakeChaseFixture();
- Assert.Same(legacy, ctl.Active);
+ bool saved = CameraDiagnostics.UseRetailChaseCamera;
+ try
+ {
+ CameraDiagnostics.UseRetailChaseCamera = false;
+ var (ctl, legacy, retail) = MakeChaseFixture();
+ Assert.Same(legacy, ctl.Active);
- CameraDiagnostics.UseRetailChaseCamera = true;
- Assert.Same(retail, ctl.Active);
+ CameraDiagnostics.UseRetailChaseCamera = true;
+ Assert.Same(retail, ctl.Active);
- CameraDiagnostics.UseRetailChaseCamera = false;
- Assert.Same(legacy, ctl.Active);
+ CameraDiagnostics.UseRetailChaseCamera = false;
+ Assert.Same(legacy, ctl.Active);
+ }
+ finally
+ {
+ CameraDiagnostics.UseRetailChaseCamera = saved;
+ }
}
[Fact]
public void ExitChaseMode_ClearsBothCameras()
{
- CameraDiagnostics.UseRetailChaseCamera = false;
- var (ctl, _, _) = MakeChaseFixture();
- ctl.ExitChaseMode();
+ bool saved = CameraDiagnostics.UseRetailChaseCamera;
+ try
+ {
+ CameraDiagnostics.UseRetailChaseCamera = false;
+ var (ctl, _, _) = MakeChaseFixture();
+ ctl.ExitChaseMode();
- Assert.Null(ctl.Chase);
- Assert.Null(ctl.RetailChase);
- Assert.False(ctl.IsChaseMode);
+ Assert.Null(ctl.Chase);
+ Assert.Null(ctl.RetailChase);
+ Assert.False(ctl.IsChaseMode);
+ }
+ finally
+ {
+ CameraDiagnostics.UseRetailChaseCamera = saved;
+ }
}
}