#nullable enable using Nox.Game; using Nox.Platform; using Nox.Game.UI; using Jovian.SaveSystem; using System.Collections.Generic; using ZLinq; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; namespace Nox.Core { /// /// The Game Mode Game State which is the base state in which all GameModes need to run. /// Game Modes are any modes that involve gameplay and gameplay related scenes. This state is where the game runs. /// public class GameModeGameState : IGameState { private readonly GameDataState gameDataState; private readonly PlatformSettings platformSettings; private readonly PlayModeSettings bootstrapSettings; private readonly ISaveSystem saveSystem; private readonly ISceneTransition sceneTransition; private readonly AdventureData adventuredata; private readonly Dictionary playModeCache = new(); private IMenuView? pauseMenuView; private IPlayMode? playMode; private IPlayMode? suspendedPlayMode; private bool isTransitioning; private PlayMode currentPlaymode; private bool stateEntered; private AsyncOperationHandle advSettingsHandler; private AdventureSettings? adventureSettings; public bool IsGameStateInitialized { get => stateEntered && (playMode == null || playMode.IsGameModeInitialized); } public GameModeGameState(GameDataState gameDataState, BootstrapReferences bootstrapReferences, PlatformSettings platformSettings, ISaveSystem saveSystem, ISceneTransition sceneTransition, AdventureData adventuredata) { this.gameDataState = gameDataState; this.platformSettings = platformSettings; this.saveSystem = saveSystem; this.sceneTransition = sceneTransition; this.adventuredata = adventuredata; bootstrapSettings = Addressables.LoadAssetAsync(bootstrapReferences.playModeSettings).WaitForCompletion(); } public void EnterGameState() { if(gameDataState.ActivePlayMode == PlayMode.None) { var sceneReference = Object.FindFirstObjectByType(); if(bootstrapSettings.gameModeData.AsValueEnumerable().Any(g => g.playMode == sceneReference.playMode)) { gameDataState.ChangePlayMode(sceneReference.playMode); } } advSettingsHandler = Addressables.LoadAssetAsync("AdventureSettings"); adventureSettings = advSettingsHandler.WaitForCompletion(); InitializeGameViews(); currentPlaymode = gameDataState.ActivePlayMode; playMode?.EnterPlayMode(); Debug.Log($"Initialized Game Mode: {gameDataState.ActivePlayMode}"); stateEntered = true; } private void InitializeGameViews() { SwitchGameMode(); pauseMenuView ??= new PauseMenuView(gameDataState, saveSystem, CaptureNoxSaveData); } private NoxSavedDataSet? CaptureNoxSaveData() { var adventure = FindAdventurePlayMode(); return adventure?.CaptureNoxSaveData(); } private AdventurePlayMode? FindAdventurePlayMode() { if(playMode is AdventurePlayMode active) { return active; } if(suspendedPlayMode is AdventurePlayMode suspended) { return suspended; } if(playModeCache.TryGetValue(PlayMode.Adventure, out var cached) && cached is AdventurePlayMode fromCache) { return fromCache; } return null; } private void SwitchGameMode() { var activeParty = gameDataState.ActiveParty; if(playModeCache.TryGetValue(gameDataState.ActivePlayMode, out var cached)) { playMode = cached; return; } playMode = gameDataState.ActivePlayMode switch { PlayMode.Adventure => new AdventurePlayMode(platformSettings, activeParty, bootstrapSettings, gameDataState, saveSystem, adventureSettings, adventuredata), PlayMode.PauseMenu => new PauseMenuPlayMode(platformSettings, gameDataState, pauseMenuView!), PlayMode.Town => new TownPlayMode(platformSettings, activeParty), PlayMode.Rest => new RestPlayMode(platformSettings, activeParty), PlayMode.Combat => new CombatPlayMode(platformSettings, activeParty), _ => playMode }; playModeCache[gameDataState.ActivePlayMode] = playMode; } public GameState Tick() { if(isTransitioning) { return GameState.GameMode; } if(ShouldTransition()) { HandlePlayModeTransition(); return GameState.GameMode; } if(playMode is { IsGameModeInitialized: true }) { playMode.Tick(); } return GameState.GameMode; } private bool ShouldTransition() { return currentPlaymode != gameDataState.ActivePlayMode && gameDataState.ActivePlayMode != PlayMode.None; } private bool IsPauseTransition() { return gameDataState.ActivePlayMode == PlayMode.PauseMenu || (currentPlaymode == PlayMode.PauseMenu && suspendedPlayMode != null); } private void HandlePlayModeTransition() { if(IsPauseTransition()) { ExecutePlayModeSwitch(); return; } isTransitioning = true; sceneTransition.FadeOut(() => { ExecutePlayModeSwitch(); sceneTransition.FadeIn(() => { isTransitioning = false; }); }); } private void ExecutePlayModeSwitch() { var enteringPause = gameDataState.ActivePlayMode == PlayMode.PauseMenu; var resumingFromPause = currentPlaymode == PlayMode.PauseMenu && suspendedPlayMode != null; if(enteringPause) { suspendedPlayMode = playMode; } playMode?.ExitGameMode(); currentPlaymode = gameDataState.ActivePlayMode; if(resumingFromPause) { playMode = suspendedPlayMode; suspendedPlayMode = null; playMode?.EnterPlayMode(); return; } SwitchGameMode(); playMode?.EnterPlayMode(); } public void LateTick() { if(playMode is { IsGameModeInitialized: true }) { playMode.LateTick(); } } public void Dispose() { suspendedPlayMode?.Dispose(); playMode?.Dispose(); } public void ExitGameState() { suspendedPlayMode?.ExitGameMode(); suspendedPlayMode?.Dispose(); suspendedPlayMode = null; playMode?.ExitGameMode(); playMode?.Dispose(); playMode = null; foreach(var cached in playModeCache.Values) { if(cached != null && cached != playMode && cached != suspendedPlayMode) { cached.Dispose(); } } playModeCache.Clear(); stateEntered = false; gameDataState.ChangePlayMode(PlayMode.None); advSettingsHandler.Release(); } } }