From 929b463c47d8e129e7e3c27edf4d26c98d9e5455 Mon Sep 17 00:00:00 2001 From: xen-42 Date: Fri, 14 Feb 2025 22:32:52 -0500 Subject: [PATCH] Add support to addon manifest --- .../Builder/Volumes/CreditsVolumeBuilder.cs | 5 +- NewHorizons/Components/NHGameOverManager.cs | 138 ++++++++++++++++++ .../Components/Volumes/LoadCreditsVolume.cs | 105 +------------ NewHorizons/External/Configs/AddonConfig.cs | 7 + NewHorizons/External/Configs/PlanetConfig.cs | 4 + .../External/Modules/GameOverModule.cs | 9 +- .../VolumeInfos/LoadCreditsVolumeInfo.cs | 5 +- NewHorizons/Main.cs | 7 + NewHorizons/Patches/DeathManagerPatches.cs | 15 ++ 9 files changed, 188 insertions(+), 107 deletions(-) create mode 100644 NewHorizons/Components/NHGameOverManager.cs create mode 100644 NewHorizons/Patches/DeathManagerPatches.cs diff --git a/NewHorizons/Builder/Volumes/CreditsVolumeBuilder.cs b/NewHorizons/Builder/Volumes/CreditsVolumeBuilder.cs index 39440b45..81c3c6ce 100644 --- a/NewHorizons/Builder/Volumes/CreditsVolumeBuilder.cs +++ b/NewHorizons/Builder/Volumes/CreditsVolumeBuilder.cs @@ -11,11 +11,8 @@ namespace NewHorizons.Builder.Volumes { var volume = VolumeBuilder.Make(planetGO, sector, info); - volume.creditsType = info.creditsType; - volume.gameOverText = info.gameOver?.text; + volume.gameOver = info.gameOver; volume.deathType = info.deathType == null ? null : EnumUtils.Parse(info.deathType.ToString(), DeathType.Default); - volume.colour = info.gameOver?.colour?.ToColor(); - volume.condition = info.gameOver?.condition; return volume; } diff --git a/NewHorizons/Components/NHGameOverManager.cs b/NewHorizons/Components/NHGameOverManager.cs new file mode 100644 index 00000000..93a7339a --- /dev/null +++ b/NewHorizons/Components/NHGameOverManager.cs @@ -0,0 +1,138 @@ +using NewHorizons.External.Modules; +using NewHorizons.External.SerializableEnums; +using NewHorizons.Handlers; +using NewHorizons.Utility.OWML; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace NewHorizons.Components +{ + public class NHGameOverManager : MonoBehaviour + { + /// + /// Mod unique id to game over module list + /// Done as a dictionary so that Reload Configs can overwrite entries per mod + /// + public static Dictionary gameOvers = new(); + + public static NHGameOverManager Instance { get; private set; } + + private GameOverController _gameOverController; + private PlayerCameraEffectController _playerCameraEffectController; + + private GameOverModule[] _gameOvers; + + private bool _gameOverSequenceStarted; + + public void Awake() + { + Instance = this; + } + + public void Start() + { + _gameOverController = FindObjectOfType(); + _playerCameraEffectController = FindObjectOfType(); + + _gameOvers = gameOvers.SelectMany(x => x.Value).ToArray(); + } + + public void TryHijackDeathSequence() + { + var gameOver = _gameOvers.FirstOrDefault(x => !string.IsNullOrEmpty(x.condition) && DialogueConditionManager.SharedInstance.GetConditionState(x.condition)); + if (!_gameOverSequenceStarted && gameOver != null && !Locator.GetDeathManager()._finishedDLC) + { + StartGameOverSequence(gameOver, null); + } + } + + public void StartGameOverSequence(GameOverModule gameOver, DeathType? deathType) + { + _gameOverSequenceStarted = true; + Delay.StartCoroutine(GameOver(gameOver, deathType)); + } + + private IEnumerator GameOver(GameOverModule gameOver, DeathType? deathType) + { + OWInput.ChangeInputMode(InputMode.None); + ReticleController.Hide(); + Locator.GetPromptManager().SetPromptsVisible(false); + Locator.GetPauseCommandListener().AddPauseCommandLock(); + + // The PlayerCameraEffectController is what actually kills us, so convince it we're already dead + Locator.GetDeathManager()._isDead = true; + + var fadeLength = 2f; + + if (Locator.GetDeathManager()._isDying) + { + // Player already died at this point, so don't fade + fadeLength = 0f; + } + else if (deathType is DeathType nonNullDeathType) + { + _playerCameraEffectController.OnPlayerDeath(nonNullDeathType); + fadeLength = _playerCameraEffectController._deathFadeLength; + } + else + { + // Wake up relaxed next loop + PlayerData.SetLastDeathType(DeathType.Meditation); + FadeHandler.FadeOut(fadeLength); + } + + yield return new WaitForSeconds(fadeLength); + + if (!string.IsNullOrEmpty(gameOver.text) && _gameOverController != null) + { + _gameOverController._deathText.text = TranslationHandler.GetTranslation(gameOver.text, TranslationHandler.TextType.UI); + _gameOverController.SetupGameOverScreen(5f); + + if (gameOver.colour != null) + { + _gameOverController._deathText.color = gameOver.colour.ToColor(); + } + + // Make sure the fade handler is off now + FadeHandler.FadeIn(0f); + + // We set this to true to stop it from loading the credits scene, so we can do it ourselves + _gameOverController._loading = true; + + yield return new WaitUntil(ReadytoLoadCreditsScene); + } + + LoadCreditsScene(gameOver); + } + + private bool ReadytoLoadCreditsScene() => _gameOverController._fadedOutText && _gameOverController._textAnimator.IsComplete(); + + private void LoadCreditsScene(GameOverModule gameOver) + { + NHLogger.LogVerbose($"Load credits {gameOver.creditsType}"); + + switch (gameOver.creditsType) + { + case NHCreditsType.Fast: + LoadManager.LoadScene(OWScene.Credits_Fast, LoadManager.FadeType.ToBlack); + break; + case NHCreditsType.Final: + LoadManager.LoadScene(OWScene.Credits_Final, LoadManager.FadeType.ToBlack); + break; + case NHCreditsType.Kazoo: + TimelineObliterationController.s_hasRealityEnded = true; + LoadManager.LoadScene(OWScene.Credits_Fast, LoadManager.FadeType.ToBlack); + break; + default: + // GameOverController disables post processing + _gameOverController._flashbackCamera.postProcessing.enabled = true; + // For some reason this isn't getting set sometimes + AudioListener.volume = 1; + GlobalMessenger.FireEvent("TriggerFlashback"); + break; + } + } + } +} diff --git a/NewHorizons/Components/Volumes/LoadCreditsVolume.cs b/NewHorizons/Components/Volumes/LoadCreditsVolume.cs index 4ea5e6cd..26f75831 100644 --- a/NewHorizons/Components/Volumes/LoadCreditsVolume.cs +++ b/NewHorizons/Components/Volumes/LoadCreditsVolume.cs @@ -1,8 +1,4 @@ -using NewHorizons.External.SerializableEnums; -using NewHorizons.Handlers; -using NewHorizons.Utility; -using NewHorizons.Utility.OWML; -using System.Collections; +using NewHorizons.External.Modules; using UnityEngine; @@ -10,108 +6,17 @@ namespace NewHorizons.Components.Volumes { internal class LoadCreditsVolume : BaseVolume { - public NHCreditsType creditsType = NHCreditsType.None; - - public string gameOverText; - public DeathType? deathType = DeathType.Default; - - public Color? colour; - public string condition; - - private GameOverController _gameOverController; - private PlayerCameraEffectController _playerCameraEffectController; - - public void Start() - { - _gameOverController = FindObjectOfType(); - _playerCameraEffectController = FindObjectOfType(); - } + public GameOverModule gameOver; + public DeathType? deathType; public override void OnTriggerVolumeEntry(GameObject hitObj) { - if (hitObj.CompareTag("PlayerDetector") && enabled && (string.IsNullOrEmpty(condition) || DialogueConditionManager.SharedInstance.GetConditionState(condition))) + if (hitObj.CompareTag("PlayerDetector") && enabled && (string.IsNullOrEmpty(gameOver.condition) || DialogueConditionManager.SharedInstance.GetConditionState(gameOver.condition))) { - // Have to run it off the mod behaviour since the game over controller disables everything - Delay.StartCoroutine(GameOver()); + NHGameOverManager.Instance.StartGameOverSequence(gameOver, deathType); } } - private IEnumerator GameOver() - { - OWInput.ChangeInputMode(InputMode.None); - ReticleController.Hide(); - Locator.GetPromptManager().SetPromptsVisible(false); - Locator.GetPauseCommandListener().AddPauseCommandLock(); - - // The PlayerCameraEffectController is what actually kills us, so convince it we're already dead - Locator.GetDeathManager()._isDead = true; - - var fadeLength = 2f; - - if (deathType is DeathType nonNullDeathType) - { - _playerCameraEffectController.OnPlayerDeath(nonNullDeathType); - fadeLength = _playerCameraEffectController._deathFadeLength; - } - else - { - // Wake up relaxed next loop - PlayerData.SetLastDeathType(DeathType.Meditation); - FadeHandler.FadeOut(fadeLength); - } - - yield return new WaitForSeconds(fadeLength); - - if (!string.IsNullOrEmpty(gameOverText) && _gameOverController != null) - { - _gameOverController._deathText.text = TranslationHandler.GetTranslation(gameOverText, TranslationHandler.TextType.UI); - _gameOverController.SetupGameOverScreen(5f); - - if (colour != null) - { - _gameOverController._deathText.color = (Color)colour; - } - - // Make sure the fade handler is off now - FadeHandler.FadeIn(0f); - - // We set this to true to stop it from loading the credits scene, so we can do it ourselves - _gameOverController._loading = true; - - yield return new WaitUntil(ReadytoLoadCreditsScene); - } - - LoadCreditsScene(); - } - - private bool ReadytoLoadCreditsScene() => _gameOverController._fadedOutText && _gameOverController._textAnimator.IsComplete(); - public override void OnTriggerVolumeExit(GameObject hitObj) { } - - private void LoadCreditsScene() - { - NHLogger.LogVerbose($"Load credits {creditsType}"); - - switch (creditsType) - { - case NHCreditsType.Fast: - LoadManager.LoadScene(OWScene.Credits_Fast, LoadManager.FadeType.ToBlack); - break; - case NHCreditsType.Final: - LoadManager.LoadScene(OWScene.Credits_Final, LoadManager.FadeType.ToBlack); - break; - case NHCreditsType.Kazoo: - TimelineObliterationController.s_hasRealityEnded = true; - LoadManager.LoadScene(OWScene.Credits_Fast, LoadManager.FadeType.ToBlack); - break; - default: - // GameOverController disables post processing - _gameOverController._flashbackCamera.postProcessing.enabled = true; - // For some reason this isn't getting set sometimes - AudioListener.volume = 1; - GlobalMessenger.FireEvent("TriggerFlashback"); - break; - } - } } } diff --git a/NewHorizons/External/Configs/AddonConfig.cs b/NewHorizons/External/Configs/AddonConfig.cs index 66674575..8254b852 100644 --- a/NewHorizons/External/Configs/AddonConfig.cs +++ b/NewHorizons/External/Configs/AddonConfig.cs @@ -1,3 +1,4 @@ +using NewHorizons.External.Modules; using NewHorizons.OtherMods.AchievementsPlus; using Newtonsoft.Json; @@ -44,5 +45,11 @@ namespace NewHorizons.External.Configs /// The dimensions of the Echoes of the Eye subtitle is 669 x 67, so aim for that size /// public string subtitlePath = "subtitle.png"; + + /// + /// Custom game over messages for this mod. This can either display a title card before looping like in EOTE, or show a message and roll credits like the various time loop escape endings. + /// You must set a dialogue condition for the game over sequence to run. + /// + public GameOverModule[] gameOver; } } diff --git a/NewHorizons/External/Configs/PlanetConfig.cs b/NewHorizons/External/Configs/PlanetConfig.cs index 65c4c8f0..40744878 100644 --- a/NewHorizons/External/Configs/PlanetConfig.cs +++ b/NewHorizons/External/Configs/PlanetConfig.cs @@ -670,6 +670,10 @@ namespace NewHorizons.External.Configs } volume.gameOver.text = volume.gameOverText; } + if (volume.creditsType != null) + { + volume.gameOver.creditsType = (SerializableEnums.NHCreditsType)volume.creditsType; + } } } diff --git a/NewHorizons/External/Modules/GameOverModule.cs b/NewHorizons/External/Modules/GameOverModule.cs index 20bd74e4..00d029bd 100644 --- a/NewHorizons/External/Modules/GameOverModule.cs +++ b/NewHorizons/External/Modules/GameOverModule.cs @@ -1,5 +1,7 @@ using NewHorizons.External.SerializableData; +using NewHorizons.External.SerializableEnums; using Newtonsoft.Json; +using System.ComponentModel; namespace NewHorizons.External.Modules { @@ -17,9 +19,14 @@ namespace NewHorizons.External.Modules public MColor colour; /// - /// Condition that must be true for this game over to trigger. Leave empty to always trigger this game over. + /// Condition that must be true for this game over to trigger. If this is on a LoadCreditsVolume, leave empty to always trigger this game over. /// Note this is a regular dialogue condition, not a persistent condition. /// public string condition; + + /// + /// The type of credits that will run after the game over message is shown + /// + [DefaultValue("fast")] public NHCreditsType creditsType = NHCreditsType.Fast; } } diff --git a/NewHorizons/External/Modules/Volumes/VolumeInfos/LoadCreditsVolumeInfo.cs b/NewHorizons/External/Modules/Volumes/VolumeInfos/LoadCreditsVolumeInfo.cs index 6866e226..fcf9b0da 100644 --- a/NewHorizons/External/Modules/Volumes/VolumeInfos/LoadCreditsVolumeInfo.cs +++ b/NewHorizons/External/Modules/Volumes/VolumeInfos/LoadCreditsVolumeInfo.cs @@ -8,9 +8,10 @@ namespace NewHorizons.External.Modules.Volumes.VolumeInfos [JsonObject] public class LoadCreditsVolumeInfo : VolumeInfo { - [DefaultValue("none")] public NHCreditsType creditsType = NHCreditsType.None; + [Obsolete("Use gameOver.creditsType")] + public NHCreditsType? creditsType; - [Obsolete("Use gameOver")] + [Obsolete("Use gameOver.text")] public string gameOverText; /// diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 468793aa..56d8d645 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -6,6 +6,7 @@ using NewHorizons.Builder.Props; using NewHorizons.Builder.Props.Audio; using NewHorizons.Builder.Props.EchoesOfTheEye; using NewHorizons.Builder.Props.TranslatorText; +using NewHorizons.Components; using NewHorizons.Components.EOTE; using NewHorizons.Components.Fixers; using NewHorizons.Components.Ship; @@ -483,6 +484,8 @@ namespace NewHorizons // Fix spawn point PlayerSpawnHandler.SetUpPlayerSpawn(); + new GameObject(nameof(NHGameOverManager)).AddComponent(); + if (isSolarSystem) { // Warp drive @@ -835,6 +838,10 @@ namespace NewHorizons AssetBundleUtilities.PreloadBundle(bundle, mod); } } + if (addonConfig.gameOver != null) + { + NHGameOverManager.gameOvers[mod.ModHelper.Manifest.UniqueName] = addonConfig.gameOver; + } AddonConfigs[mod] = addonConfig; } diff --git a/NewHorizons/Patches/DeathManagerPatches.cs b/NewHorizons/Patches/DeathManagerPatches.cs new file mode 100644 index 00000000..40f7b14b --- /dev/null +++ b/NewHorizons/Patches/DeathManagerPatches.cs @@ -0,0 +1,15 @@ +using HarmonyLib; +using NewHorizons.Components; + +namespace NewHorizons.Patches; + +[HarmonyPatch] +public static class DeathManagerPatches +{ + [HarmonyPrefix] + [HarmonyPatch(typeof(DeathManager), nameof(DeathManager.FinishDeathSequence))] + public static void DeathManager_FinishDeathSequence() + { + NHGameOverManager.Instance.TryHijackDeathSequence(); + } +}