using System.Collections.Generic; using System.Text.Json; using Godot; using Mr.BrickAdventures.scripts; using Mr.BrickAdventures.scripts.State; namespace Mr.BrickAdventures.Autoloads; public partial class GhostManager : Node { public static GhostManager Instance { get; private set; } [Export] private PackedScene GhostPlayerScene { get; set; } public override void _Ready() => Instance = this; public override void _ExitTree() { if (Instance == this) Instance = null; } public bool IsRecording { get; private set; } = false; public bool IsPlaybackEnabled { get; private set; } = true; private List _currentRecording = []; private double _startTime = 0.0; private int _currentLevelIndex = -1; public void StartRecording(int levelIndex) { if (!IsPlaybackEnabled) return; _currentLevelIndex = levelIndex; _currentRecording.Clear(); _startTime = Time.GetTicksMsec() / 1000.0; IsRecording = true; GD.Print("Ghost recording started."); } public void StopRecording(bool levelCompleted, double finalTime) { if (!IsRecording) return; IsRecording = false; if (levelCompleted) { var bestTime = LoadBestTime(_currentLevelIndex); if (finalTime < bestTime) { SaveGhostData(_currentLevelIndex, finalTime); GD.Print($"New best ghost saved for level {_currentLevelIndex}. Time: {finalTime}"); } } _currentRecording.Clear(); } public void RecordFrame(Vector2 position) { if (!IsRecording) return; _currentRecording.Add(new GhostFrame { Timestamp = (Time.GetTicksMsec() / 1000.0) - _startTime, Position = position }); } public void SpawnGhostPlayer(int levelIndex, Node parent) { if (!IsPlaybackEnabled || GhostPlayerScene == null) return; var ghostData = LoadGhostData(levelIndex); if (ghostData.Count > 0) { var ghostPlayer = GhostPlayerScene.Instantiate(); parent.AddChild(ghostPlayer); ghostPlayer.StartPlayback(ghostData); GD.Print($"Ghost player spawned for level {levelIndex}."); } } private void SaveGhostData(int levelIndex, double time) { var path = $"user://ghost_level_{levelIndex}.json"; var saveData = new GhostSaveData { BestTime = time }; foreach (var frame in _currentRecording) saveData.Frames.Add(new GhostFrameDto { Timestamp = frame.Timestamp, X = frame.Position.X, Y = frame.Position.Y }); try { var json = JsonSerializer.Serialize(saveData, SaveSystem.JsonOptions); using var file = FileAccess.Open(path, FileAccess.ModeFlags.Write); file.StoreString(json); } catch (System.Exception e) { GD.PrintErr($"GhostManager: Failed to save ghost data: {e.Message}"); } } private List LoadGhostData(int levelIndex) { var path = $"user://ghost_level_{levelIndex}.json"; if (!FileAccess.FileExists(path)) return []; try { using var file = FileAccess.Open(path, FileAccess.ModeFlags.Read); var saveData = JsonSerializer.Deserialize(file.GetAsText(), SaveSystem.JsonOptions); if (saveData == null) return []; var frames = new List(); foreach (var dto in saveData.Frames) frames.Add(new GhostFrame { Timestamp = dto.Timestamp, Position = new Vector2(dto.X, dto.Y) }); return frames; } catch (System.Exception e) { GD.PrintErr($"GhostManager: Failed to load ghost data: {e.Message}"); return []; } } private double LoadBestTime(int levelIndex) { var path = $"user://ghost_level_{levelIndex}.json"; if (!FileAccess.FileExists(path)) return double.MaxValue; try { using var file = FileAccess.Open(path, FileAccess.ModeFlags.Read); var saveData = JsonSerializer.Deserialize(file.GetAsText(), SaveSystem.JsonOptions); return saveData?.BestTime ?? double.MaxValue; } catch { return double.MaxValue; } } }