#nullable enable using System.Globalization; using Godot; using SideScrollerGame.Debug.Commands; namespace SideScrollerGame.Debug; public partial class DebugOverlay : CanvasLayer { public override void _Ready() { Layer = 100; ProcessMode = ProcessModeEnum.Always; EnsureLabel(); } public void SetStatus(DebugSettings settings, string loadedSceneId) { m_Settings = settings; m_LoadedSceneId = loadedSceneId; Label label = EnsureLabel(); label.Text = $"Debug boot: {settings.BootMode}\nSeed: {settings.Seed}\nScene: {loadedSceneId}\nDebug: {OS.IsDebugBuild()}"; } public void Bind(DebugCommandService service, DebugSettings settings, string loadedSceneId) { m_Service = service; m_Settings = settings; m_LoadedSceneId = loadedSceneId; service.StateChanged += _ => Refresh(); Refresh(); } public void SetHeroSummary(string summary) { m_HeroSummary = summary; Refresh(); } private void Refresh() { Label label = EnsureLabel(); if (m_Service is null || m_Settings is null) { return; } DebugRuntimeState state = m_Service.State; Visible = state.OverlayVisible; string heroLine = string.IsNullOrWhiteSpace(m_HeroSummary) ? string.Empty : $"\nHero: {m_HeroSummary}"; label.Text = $"Debug boot: {m_Settings.BootMode}\n" + $"Scene: {m_LoadedSceneId}\n" + $"Seed: {state.Seed}\n" + $"Difficulty: {state.ActiveDifficultyId}\n" + $"Paused: {state.IsPaused}\n" + $"Time scale: {state.TimeScale.ToString(CultureInfo.InvariantCulture)}\n" + $"Marker: {DisplayOrNone(state.CurrentMarkerId)}\n" + $"Spawned: {state.SpawnedActorCount} ({DisplayOrNone(state.LastSpawnedActorId)})\n" + $"Flags: invuln={state.Invulnerable}, ammo={state.InfiniteSpecialAmmo}, nofire={state.NoEnemyFire}\n" + $"Debug draw: collisions={state.ShowCollisionShapes}, bounds={state.ShowGameplayBounds}" + heroLine; } private Label EnsureLabel() { if (m_StatusLabel is not null) { return m_StatusLabel; } m_StatusLabel = GetNodeOrNull