using ADOFAIRenderer.Core; using HarmonyLib; namespace ADOFAIRenderer.Patch.Game; /// /// Keeps the game controller (scrController) in uninterrupted play during a render: no pause, no failure, /// no scene change after the clear, and no clear texts. /// /// All patches are active while (stage >= OnScene). RenderManager also sets /// noFail, noFailInfiniteMargin and disableCongratsMessage on the controller when the scene is ready. /// /// [HarmonyPatch(typeof(scrController))] // ReSharper disable once InconsistentNaming public static class scrControllerPatch { /// /// Prefix of scrController.TogglePauseGame() (pause menu toggle; sets Time.timeScale to 0 or 1 and shows the /// pause menu; also called from the pause button, the pause key and OnApplicationPauseCallback). Skipped while /// rendering, so the level cannot be paused. The skipped call returns /// false (Harmony default for the unset bool result). /// /// True to run the original method (not rendering); false to skip it (rendering). [HarmonyPrefix] [HarmonyPatch("TogglePauseGame")] public static bool PreventPause() { return !RenderManager.IsActive; } /// /// Prefix of scrController.PortalTravelAction(Portal), which leaves the level through a portal (next level, /// level select, main menu, ...); the game calls it on entering the Won state for non-world levels and on input after a win. /// Skipped while rendering, so the scene stays loaded for the delay after the level end. /// /// True to run the original method (not rendering); false to skip it (rendering). [HarmonyPrefix] [HarmonyPatch("PortalTravelAction")] public static bool PreventPortalTravel() { return !RenderManager.IsActive; } /// /// Prefix of scrController.FailAction(...), which enters the Fail state: stops the song and all sounds, pauses the /// VFX video and shows the failure texts. Skipped while rendering. /// /// True to run the original method (not rendering); false to skip it (rendering). [HarmonyPrefix] [HarmonyPatch("FailAction")] public static bool PreventFailAction() { return !RenderManager.IsActive; } /// /// Prefix of scrController.Fail2Action(), which moves from Fail to Fail2 (accuracy calculation, saving of the /// best result for boss/custom boss levels, death counter). Skipped while rendering. /// /// True to run the original method (not rendering); false to skip it (rendering). [HarmonyPrefix] [HarmonyPatch("Fail2Action")] public static bool PreventFail2Action() { return !RenderManager.IsActive; } /// /// Prefix of scrController.OnPlayerDied(...), which kills the remaining players and calls FailAction when no other /// player is alive. Skipped while rendering. /// /// True to run the original method (not rendering); false to skip it (rendering). [HarmonyPrefix] [HarmonyPatch("OnPlayerDied")] public static bool PreventPlayerDied() { return !RenderManager.IsActive; } /// /// Postfix of scrController.OnLandOnPortal(...), the win handler on the last tile: it sets and activates /// txtCongrats / txtAprilCongrats / txtAllStrictClear, may show the detailed results and end-screen /// lanterns, saves the result and changes the state to Won. /// /// While rendering, clears and deactivates the three clear texts and disables the detailed results panel after the /// original has run, so they do not appear in the rendered video. The Won state itself is kept: the default render end /// condition (RenderContext.EndCondition) waits for it. /// /// /// The game controller. [HarmonyPostfix] [HarmonyPatch("OnLandOnPortal")] public static void PreventWinTextsPostfix(scrController __instance) { if (!RenderManager.IsActive || __instance == null) return; if (__instance.txtCongrats) { __instance.txtCongrats.text = string.Empty; __instance.txtCongrats.gameObject.SetActive(false); } if (__instance.txtAprilCongrats) { __instance.txtAprilCongrats.text = string.Empty; __instance.txtAprilCongrats.gameObject.SetActive(false); } if (__instance.txtAllStrictClear) { __instance.txtAllStrictClear.text = string.Empty; __instance.txtAllStrictClear.gameObject.SetActive(false); } if (__instance.detailedResults) { __instance.detailedResults.enabled = false; if (__instance.detailedResults.gameObject != null) { __instance.detailedResults.gameObject.SetActive(false); } } } }