using Chickensoft.AutoInject; using Chickensoft.Collections; using Chickensoft.Introspection; using Godot; using Godot.Collections; using System; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using Zennysoft.Game.Abstractions; namespace Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] public partial class Map : Node3D, IMap { public override void _Notification(int what) => this.Notify(what); [Dependency] public IGame Game => this.DependOn(); [Node] public AnimationPlayer AnimationPlayer { get; set; } = default!; public IDungeonFloor CurrentFloor { get; private set; } public event Action<(Vector3 Rotation, Vector3 Position)> SpawnPointCreated; public Array FloorRoute { get; private set; } [Export] public Array BaseRoute { get; set; } [Export] public Array NormalEndRoute { get; set; } [Export] public Array TrueEndRoute { get; set; } [Export] public Array BadEndRoute { get; set; } public AutoProp CurrentSelectedFloor { get; set; } public event Action FloorLoaded; private string _floorToLoad; private Stopwatch _elapsedTime; public override void _EnterTree() { FloorRoute = [.. BaseRoute]; CurrentSelectedFloor = new AutoProp(FloorRoute.First()); } public void OnResolved() { AnimationPlayer.AnimationFinished += AnimationPlayer_AnimationFinished; this.Provide(); } private async void AnimationPlayer_AnimationFinished(StringName animName) { if (animName == "fade_out") await ContinueLoadingFloor(_floorToLoad); } public void ShowMap() => FadeIn(); private void FadeIn() => AnimationPlayer.Play("fade_in"); private void FadeOut() => AnimationPlayer.Play("fade_out"); public void EndCurrentGame() { CleanupCurrentFloor(); ClearFloor(); FloorRoute = [.. BaseRoute]; } public async Task LoadFloor(FloorScene floorToLoad) { _elapsedTime = new Stopwatch(); _elapsedTime.Start(); Game.Pause(); // Clean up current floor CleanupCurrentFloor(); // Start loading new floor var filePath = FloorScenePathConverter.Convert(floorToLoad); _floorToLoad = filePath; FadeOut(); } public async Task LoadFloorByPath(string filePath) { Game.Pause(); // Clean up current floor CleanupCurrentFloor(); // Start loading new floor _floorToLoad = filePath; FadeOut(); } public void AddNormalRoute() { FloorRoute.AddRange(NormalEndRoute); } public void AddTrueRoute() { FloorRoute.AddRange(TrueEndRoute); } public void AddBadEndRoute() { FloorRoute.AddRange(BadEndRoute); } public void ResetToBaseRoute() { FloorRoute = [.. BaseRoute]; CurrentSelectedFloor = new AutoProp(FloorRoute.First()); } private async Task ContinueLoadingFloor(string filePath) { SpawnPointCreated?.Invoke((Vector3.Forward, new Vector3(-999, -999, -999))); var newFloor = await LoadSceneFile(filePath); // New floor created, remove old floor ClearFloor(); // Setup new floor AddChild(newFloor); CurrentFloor = newFloor as IDungeonFloor; CurrentFloor.InitializeDungeon(); SpawnPointCreated?.Invoke(CurrentFloor.GetPlayerSpawnPoint()); if (CurrentFloor is SpecialFloor) Game.ShowMinimap(false); else Game.ShowMinimap(true); SpawnEnemies(); SpawnItems(); CurrentFloor.FloorIsLoaded = true; FloorLoaded?.Invoke(); _elapsedTime.Stop(); GD.Print($"Floor loaded in {_elapsedTime.ElapsedMilliseconds}ms."); } private void CleanupCurrentFloor() { var dimmableAudio = GetTree().GetNodesInGroup("DimmableAudio").OfType(); foreach (var node in dimmableAudio) node.FadeOut(); } private void ClearFloor() { try { if (CurrentFloor != null && !CurrentFloor.IsQueuedForDeletion()) CurrentFloor?.CallDeferred(MethodName.QueueFree, []); } catch (ObjectDisposedException ex) { } } private async Task LoadSceneFile(string sceneName) { var sceneLoader = new SceneLoader(); AddChild(sceneLoader); sceneLoader.LoadSceneRequest(sceneName); await ToSignal(sceneLoader, SceneLoader.SignalName.SceneLoaded); var result = sceneLoader.LoadedScene; sceneLoader.QueueFree(); return result; } private void SpawnEnemies() { var monsterRooms = CurrentFloor.Rooms.OfType(); foreach (var room in monsterRooms) room.SpawnEnemies(CurrentSelectedFloor.Value.EnemySpawnTable); } private void SpawnItems() { var monsterRooms = CurrentFloor.Rooms.OfType(); foreach (var room in monsterRooms) room.SpawnItems(CurrentSelectedFloor.Value.FloorType); } }