diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index 9773a9bcc..c6aa977e2 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -1827,6 +1827,12 @@ static void RequireDaemonRenderQueryAuthority(string root) "public double HeatstrokePhasingFrequency { get; }", "public double TargetSpottedBlinkFrequency { get; }", "public double TargetSpottedBlinkOffset { get; }", + "public IReadOnlyList MinimapZoomLevels { get; }", + "public int DefaultMinimapZoom { get; }", + "public int ResolveDefaultMinimapZoomIndex()", + "public int ResolveNextMinimapZoomIndex(int currentIndex)", + "public double ResolveMinimapDistance(int zoomIndex)", + "public double ResolveDefaultMinimapDistance()", "public double NormalizeThermalRisk(double temperature)", "public double NormalizeHeatstrokePost(double heatstroke)", "public double NormalizeSevereHeatstrokePost(double heatstroke)", @@ -1989,6 +1995,13 @@ static void RequireDaemonRenderQueryAuthority(string root) "ZoneRenderer must query daemon visibility through shared render settings instead of Unity GameplaySettings."); } + if (zoneRenderer.Contains("Settings.MinimapZoomLevels", StringComparison.Ordinal) || + zoneRenderer.Contains("Settings.DefaultMinimapZoom", StringComparison.Ordinal)) + { + throw new InvalidOperationException( + "ZoneRenderer must initialize minimap distance through shared daemon render settings instead of Unity GameSettings."); + } + if (zoneRenderer.Contains("Zone.GetOrbitPosition(", StringComparison.Ordinal)) { throw new InvalidOperationException( @@ -2128,6 +2141,7 @@ static void RequireDaemonRenderQueryAuthority(string root) "AetheriaRuntimeDaemonRenderQueries.QueryCompassMarkers(", "AetheriaRuntimeDaemonRenderQueries.QueryVisibleEntityIndices(", "RenderSettings.TargetDetectionInfoThreshold", + "RenderSettings.ResolveDefaultMinimapDistance()", "AetheriaRuntimeDaemonRenderQueries.QueryWormholeExits(", "AddWormhole(exit)", "public void AddWormhole(AetheriaRuntimeDaemonWormholeExit exit)", @@ -2211,6 +2225,9 @@ static void RequireDaemonRenderQueryAuthority(string root) "renderSettings.ResolveLockIndicatorNoiseAmplitude(", "renderSettings.ResolveLockIndicatorNoiseFrequency(", "renderSettings.ResolveLockSpinSpeed(", + "ZoneRenderer.RenderSettings.ResolveDefaultMinimapZoomIndex()", + "ZoneRenderer.RenderSettings.ResolveNextMinimapZoomIndex(_zoomLevelIndex)", + "ZoneRenderer.RenderSettings.ResolveMinimapDistance(_zoomLevelIndex)", "new AetheriaRuntimeExponentialLerp(", "Settings.GameplaySettings.TargetDetectionInfoThreshold", "Settings.GameplaySettings.SevereHeatstrokeRiskThreshold", @@ -2218,7 +2235,9 @@ static void RequireDaemonRenderQueryAuthority(string root) "Settings.HeatstrokePhasingFloor", "Settings.HeatstrokePhasingFrequency", "TargetSpottedBlinkFrequency", - "TargetSpottedBlinkOffset" + "TargetSpottedBlinkOffset", + "Settings.MinimapZoomLevels", + "Settings.DefaultMinimapZoom" }; var missingActionGameManagerRenderSymbols = requiredActionGameManagerRenderSymbols @@ -2253,6 +2272,18 @@ static void RequireDaemonRenderQueryAuthority(string root) "ActionGameManager render loops must interpret observed state through shared daemon render settings instead of Unity GameSettings: " + string.Join(", ", renderLoopHits.Select(hit => $"{hit.MethodName}:{hit.LineNumber}:{hit.Line.Trim()}"))); } + + var minimapBootHits = FindMethodScopedLineHits( + actionGameManager, + new[] { "Settings.MinimapZoomLevels", "Settings.DefaultMinimapZoom" }) + .Where(hit => hit.MethodName == "Start") + .ToArray(); + if (minimapBootHits.Length > 0) + { + throw new InvalidOperationException( + "ActionGameManager must bootstrap minimap zoom through shared daemon render settings instead of indexing Unity GameSettings directly: " + + string.Join(", ", minimapBootHits.Select(hit => $"{hit.MethodName}:{hit.LineNumber}:{hit.Line.Trim()}"))); + } } static void RequireTypedZoneConstructionKeys(string root) diff --git a/Assets/Scripts/Gameplay/ActionGameManager.cs b/Assets/Scripts/Gameplay/ActionGameManager.cs index 35824fc16..d70c6625c 100644 --- a/Assets/Scripts/Gameplay/ActionGameManager.cs +++ b/Assets/Scripts/Gameplay/ActionGameManager.cs @@ -586,7 +586,9 @@ public class ActionGameManager : MonoBehaviour Settings.HeatstrokePhasingFloor, Settings.HeatstrokePhasingFrequency, TargetSpottedBlinkFrequency, - TargetSpottedBlinkOffset); + TargetSpottedBlinkOffset, + Settings.MinimapZoomLevels?.Select(level => (double)level).ToArray(), + Settings.DefaultMinimapZoom); } private readonly (float2 direction, string name)[] _directions = { @@ -2020,6 +2022,8 @@ public class ActionGameManager : MonoBehaviour Debug.Log); LoadRuntimeLoadoutTemplates(stateBoot.StateFilePath); ZoneRenderer.RenderSettings = ObservedDaemonRenderSettings(); + _zoomLevelIndex = ZoneRenderer.RenderSettings.ResolveDefaultMinimapZoomIndex(); + ZoneRenderer.MinimapDistance = (float)ZoneRenderer.RenderSettings.ResolveMinimapDistance(_zoomLevelIndex); // If hiding minimap asteroids, turn them off to start with if (!RuntimePlayerSettings.GraphicsSettings.ShowAsteroidsInMinimap) @@ -2035,11 +2039,10 @@ public class ActionGameManager : MonoBehaviour InputDisplayLayout.Input = Input.asset; Input.Global.Enable(); - _zoomLevelIndex = Settings.DefaultMinimapZoom; Input.Player.MinimapZoom.performed += context => { - _zoomLevelIndex = (_zoomLevelIndex + 1) % Settings.MinimapZoomLevels.Length; - ZoneRenderer.MinimapDistance = Settings.MinimapZoomLevels[_zoomLevelIndex]; + _zoomLevelIndex = ZoneRenderer.RenderSettings.ResolveNextMinimapZoomIndex(_zoomLevelIndex); + ZoneRenderer.MinimapDistance = (float)ZoneRenderer.RenderSettings.ResolveMinimapDistance(_zoomLevelIndex); }; Input.Global.ZoneMap.performed += context => diff --git a/Assets/Scripts/Zone Display/ZoneRenderer.cs b/Assets/Scripts/Zone Display/ZoneRenderer.cs index c281d1633..6889e2d98 100644 --- a/Assets/Scripts/Zone Display/ZoneRenderer.cs +++ b/Assets/Scripts/Zone Display/ZoneRenderer.cs @@ -208,7 +208,7 @@ public class ZoneRenderer : MonoBehaviour var bigBounds = new Bounds(Vector3.zero, Vector3.one * 1024); foreach (var mesh in AsteroidMeshes) mesh.Mesh.bounds = bigBounds; ViewDistance = Settings.DefaultViewDistance; - MinimapDistance = Settings.MinimapZoomLevels[Settings.DefaultMinimapZoom]; + MinimapDistance = (float)RenderSettings.ResolveDefaultMinimapDistance(); _tourTimer = TourSwitchTime; _transposer = SceneCamera.GetCinemachineComponent(); diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonRenderQueries.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonRenderQueries.cs index 560ad13b2..37ce442bc 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonRenderQueries.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonRenderQueries.cs @@ -62,7 +62,9 @@ namespace GameCult.Aetheria.State.Verse double heatstrokePhasingFloor = 0.0, double heatstrokePhasingFrequency = 5.0, double targetSpottedBlinkFrequency = 20.0, - double targetSpottedBlinkOffset = -0.25) + double targetSpottedBlinkOffset = -0.25, + IReadOnlyList? minimapZoomLevels = null, + int defaultMinimapZoom = 0) { TemperatureEmissionCurve = temperatureEmissionCurve; LockIndicatorFrequency = lockIndicatorFrequency; @@ -77,6 +79,8 @@ namespace GameCult.Aetheria.State.Verse HeatstrokePhasingFrequency = heatstrokePhasingFrequency; TargetSpottedBlinkFrequency = targetSpottedBlinkFrequency; TargetSpottedBlinkOffset = targetSpottedBlinkOffset; + MinimapZoomLevels = CopyPositiveMinimapZoomLevels(minimapZoomLevels); + DefaultMinimapZoom = defaultMinimapZoom; } public AetheriaRuntimeExponentialCurve TemperatureEmissionCurve { get; } @@ -92,6 +96,36 @@ namespace GameCult.Aetheria.State.Verse public double HeatstrokePhasingFrequency { get; } public double TargetSpottedBlinkFrequency { get; } public double TargetSpottedBlinkOffset { get; } + public IReadOnlyList MinimapZoomLevels { get; } + public int DefaultMinimapZoom { get; } + + public int ResolveDefaultMinimapZoomIndex() + { + var levels = ResolveMinimapZoomLevels(); + return ClampIndex(DefaultMinimapZoom, levels.Count); + } + + public int ResolveNextMinimapZoomIndex(int currentIndex) + { + var levels = ResolveMinimapZoomLevels(); + if (levels.Count == 0) + return 0; + + return currentIndex < 0 + ? ResolveDefaultMinimapZoomIndex() + : (currentIndex + 1) % levels.Count; + } + + public double ResolveMinimapDistance(int zoomIndex) + { + var levels = ResolveMinimapZoomLevels(); + return levels[ClampIndex(zoomIndex, levels.Count)]; + } + + public double ResolveDefaultMinimapDistance() + { + return ResolveMinimapDistance(ResolveDefaultMinimapZoomIndex()); + } public double NormalizeThermalRisk(double temperature) { @@ -182,6 +216,39 @@ namespace GameCult.Aetheria.State.Verse { return minimum + Saturate(value) * (maximum - minimum); } + + private IReadOnlyList ResolveMinimapZoomLevels() + { + return MinimapZoomLevels != null && MinimapZoomLevels.Count > 0 + ? MinimapZoomLevels + : DefaultMinimapZoomLevels; + } + + private static int ClampIndex(int index, int count) + { + if (count <= 1) + return 0; + + return Math.Max(0, Math.Min(count - 1, index)); + } + + private static IReadOnlyList CopyPositiveMinimapZoomLevels(IReadOnlyList? levels) + { + if (levels == null || levels.Count == 0) + return DefaultMinimapZoomLevels; + + var copy = new List(levels.Count); + for (var index = 0; index < levels.Count; index++) + { + var level = levels[index]; + if (level > 0 && !double.IsNaN(level) && !double.IsInfinity(level)) + copy.Add(level); + } + + return copy.Count > 0 ? copy.ToArray() : DefaultMinimapZoomLevels; + } + + private static readonly IReadOnlyList DefaultMinimapZoomLevels = new[] { 1000.0 }; } public readonly struct AetheriaRuntimeGravityInfluenceBrush