SHA256
Document Harmony Patches
This commit is contained in:
1 parent
8deb64fd4d
commit
d2bd655118
19 files changed
+499
-4
No files matched your search
@@ -3,10 +3,31 @@ using HarmonyLib;
|
|||||||
|
|
||||||
namespace ADOFAIRenderer.Patch.Engine;
|
namespace ADOFAIRenderer.Patch.Engine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Makes the async input clock offset follow the render clock immediately.
|
||||||
|
/// <para>
|
||||||
|
/// Patched member: <c>AsyncInputUtils.UpdateOffsetTime(long fixDivider = 1)</c>. The async input system maps wall-clock ticks
|
||||||
|
/// (<c>AsyncInputManager.currFrameTick</c> = <c>DateTime.Now.Ticks</c>, 100 ns units) to conductor time with
|
||||||
|
/// <c>AsyncInputManager.offsetTick</c> = currFrameTick - <c>scrConductor.dspTime</c> * 10^7. With <c>fixDivider</c> 1 the
|
||||||
|
/// offset is set directly; otherwise it moves by 1 / fixDivider of the difference per call. <c>scrConductor</c> calls it every
|
||||||
|
/// frame with divider 100 (damped); <c>scrController</c> calls it with divider 1.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// Active while <see cref="RenderManager.IsActive"/> (stage >= OnScene). From stage Render the conductor time follows the
|
||||||
|
/// patched <c>AudioSettings.dspTime</c> (offline render clock, a fixed step per frame independent of wall-clock time), so the
|
||||||
|
/// difference changes every frame; the damped update would lag behind it. The prefix always applies the divider-1 update.
|
||||||
|
/// </para>
|
||||||
|
/// <para>Reference: https://learn.microsoft.com/en-us/dotnet/api/system.datetime.ticks</para>
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(AsyncInputUtils), "UpdateOffsetTime")]
|
[HarmonyPatch(typeof(AsyncInputUtils), "UpdateOffsetTime")]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public static class AsyncInputPatches
|
public static class AsyncInputPatches
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Prefix of <c>AsyncInputUtils.UpdateOffsetTime</c>. While rendering, sets <c>offsetTick</c> to
|
||||||
|
/// currFrameTick - dspTime * 10^7 (10^7 = 100 ns ticks per second) and <c>offsetTickUpdated</c> to true, for any divider.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True to run the original method (not rendering); false to skip it (rendering).</returns>
|
||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public static bool Prefix()
|
public static bool Prefix()
|
||||||
|
|||||||
@@ -6,13 +6,40 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace ADOFAIRenderer.Patch.Engine;
|
namespace ADOFAIRenderer.Patch.Engine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unity engine patches that put the game on the offline render clock and the render resolution.
|
||||||
|
/// <para>
|
||||||
|
/// Render clock: while capturing (<see cref="RenderManager.IsCapturingActive"/>, stage >= Render) and
|
||||||
|
/// <c>RenderContext.TimePatch</c> is on (default on; a <c>RenderSession</c> can turn it off), the Unity time getters return
|
||||||
|
/// the value captured at the capture start (<c>RenderManager.timeBase</c>, <c>unscaledTimeBase</c>, <c>dspTimeBase</c>,
|
||||||
|
/// <c>frameCountBase</c>, read in <c>SetupPlay</c>) plus the render time <see cref="TimeSpent"/>. Each rendered frame then
|
||||||
|
/// advances game time by exactly 1 / RenderFPS seconds, independent of how long the frame took to render and encode, and the
|
||||||
|
/// clocks continue from their real values without a jump. The patches only affect managed callers of these getters
|
||||||
|
/// (game and mod scripts).
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// All prefixes run on the thread that calls the getter (normally the Unity main thread) and read <c>RenderManager</c> state
|
||||||
|
/// without locks.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
public static class RenderEnvironmentPatches
|
public static class RenderEnvironmentPatches
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Render time of the current frame in seconds: <c>RenderManager.frameCount</c> / <c>RenderFPS</c>.
|
||||||
|
/// frameCount is reset to 0 when capture starts and incremented at the start of every frame (FirstUpdateHook).
|
||||||
|
/// </summary>
|
||||||
private static double TimeSpent => RenderManager.Instance.frameCount / RenderManager.Instance.Configuration.RenderFPS;
|
private static double TimeSpent => RenderManager.Instance.frameCount / RenderManager.Instance.Configuration.RenderFPS;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>Time.time</c> (scaled game time in seconds, float): returns timeBase + <see cref="TimeSpent"/> while capturing with
|
||||||
|
/// the time patch on. <c>Time.timeScale</c> is not applied to the render time.
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(Time), "time", MethodType.Getter)]
|
[HarmonyPatch(typeof(Time), "time", MethodType.Getter)]
|
||||||
public static class TimePatch
|
public static class TimePatch
|
||||||
{
|
{
|
||||||
|
/// <summary>Prefix of the <c>Time.time</c> getter.</summary>
|
||||||
|
/// <param name="__result">Replacement value in seconds.</param>
|
||||||
|
/// <returns>True to run the original getter; false when the render clock value is returned.</returns>
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
public static bool Prefix(ref float __result)
|
public static bool Prefix(ref float __result)
|
||||||
{
|
{
|
||||||
@@ -22,9 +49,13 @@ public static class RenderEnvironmentPatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary><c>Time.timeAsDouble</c> (double precision of <c>Time.time</c>): timeBase + <see cref="TimeSpent"/>.</summary>
|
||||||
[HarmonyPatch(typeof(Time), "timeAsDouble", MethodType.Getter)]
|
[HarmonyPatch(typeof(Time), "timeAsDouble", MethodType.Getter)]
|
||||||
public static class TimeAsDoublePatch
|
public static class TimeAsDoublePatch
|
||||||
{
|
{
|
||||||
|
/// <summary>Prefix of the getter; returns the render clock value while capturing with the time patch on.</summary>
|
||||||
|
/// <param name="__result">Replacement value in seconds.</param>
|
||||||
|
/// <returns>True to run the original getter; false when the render clock value is returned.</returns>
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
public static bool Prefix(ref double __result)
|
public static bool Prefix(ref double __result)
|
||||||
{
|
{
|
||||||
@@ -34,9 +65,18 @@ public static class RenderEnvironmentPatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>Time.unscaledTime</c> (time in seconds independent of <c>Time.timeScale</c>, float): unscaledTimeBase +
|
||||||
|
/// <see cref="TimeSpent"/>. Unscaled time is not advanced by <c>Time.captureDeltaTime</c> (it follows real time), so it is
|
||||||
|
/// replaced as well.
|
||||||
|
/// Reference: https://docs.unity3d.com/ScriptReference/Time-captureDeltaTime.html
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(Time), "unscaledTime", MethodType.Getter)]
|
[HarmonyPatch(typeof(Time), "unscaledTime", MethodType.Getter)]
|
||||||
public static class UnscaledTimePatch
|
public static class UnscaledTimePatch
|
||||||
{
|
{
|
||||||
|
/// <summary>Prefix of the getter; returns the render clock value while capturing with the time patch on.</summary>
|
||||||
|
/// <param name="__result">Replacement value in seconds.</param>
|
||||||
|
/// <returns>True to run the original getter; false when the render clock value is returned.</returns>
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
public static bool Prefix(ref float __result)
|
public static bool Prefix(ref float __result)
|
||||||
{
|
{
|
||||||
@@ -46,9 +86,15 @@ public static class RenderEnvironmentPatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>Time.unscaledTimeAsDouble</c> (double precision of <c>Time.unscaledTime</c>): unscaledTimeBase + <see cref="TimeSpent"/>.
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(Time), "unscaledTimeAsDouble", MethodType.Getter)]
|
[HarmonyPatch(typeof(Time), "unscaledTimeAsDouble", MethodType.Getter)]
|
||||||
public static class UnscaledTimeAsDoublePatch
|
public static class UnscaledTimeAsDoublePatch
|
||||||
{
|
{
|
||||||
|
/// <summary>Prefix of the getter; returns the render clock value while capturing with the time patch on.</summary>
|
||||||
|
/// <param name="__result">Replacement value in seconds.</param>
|
||||||
|
/// <returns>True to run the original getter; false when the render clock value is returned.</returns>
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
public static bool Prefix(ref double __result)
|
public static bool Prefix(ref double __result)
|
||||||
{
|
{
|
||||||
@@ -58,9 +104,17 @@ public static class RenderEnvironmentPatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>AudioSettings.dspTime</c> (audio system clock in seconds, double): dspTimeBase + <see cref="TimeSpent"/>.
|
||||||
|
/// The game schedules the song and computes the song position from this clock (<c>scrConductor</c>), so the level advances
|
||||||
|
/// by one render frame per frame. The offline audio mixer uses the same base (<c>RenderManager.dspTimeBase</c>).
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(AudioSettings), "dspTime", MethodType.Getter)]
|
[HarmonyPatch(typeof(AudioSettings), "dspTime", MethodType.Getter)]
|
||||||
public static class DspTimePatch
|
public static class DspTimePatch
|
||||||
{
|
{
|
||||||
|
/// <summary>Prefix of the getter; returns the render clock value while capturing with the time patch on.</summary>
|
||||||
|
/// <param name="__result">Replacement value in seconds.</param>
|
||||||
|
/// <returns>True to run the original getter; false when the render clock value is returned.</returns>
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
public static bool Prefix(ref double __result)
|
public static bool Prefix(ref double __result)
|
||||||
{
|
{
|
||||||
@@ -70,9 +124,19 @@ public static class RenderEnvironmentPatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>AudioSource.PlayScheduled(double time)</c> (start playback at a dspTime): blocked for every AudioSource while
|
||||||
|
/// <see cref="RenderManager.IsActive"/> (stage >= OnScene), so Unity plays no scheduled sound in real time. During
|
||||||
|
/// capture the song and the sound effects are produced offline by <c>OfflineAudioMixer</c>; sound effects reach it through
|
||||||
|
/// the <c>AudioManager._Play</c> and <c>scrConductor.PlayWithEndTime</c> patches (AudioPatches.cs).
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(AudioSource), "PlayScheduled")]
|
[HarmonyPatch(typeof(AudioSource), "PlayScheduled")]
|
||||||
public static class PlayScheduledPatch
|
public static class PlayScheduledPatch
|
||||||
{
|
{
|
||||||
|
/// <summary>Prefix of <c>AudioSource.PlayScheduled</c>.</summary>
|
||||||
|
/// <param name="__instance">The AudioSource (unused).</param>
|
||||||
|
/// <param name="time">Scheduled start in dspTime seconds (unused).</param>
|
||||||
|
/// <returns>True to run the original method (not rendering); false to skip it (rendering).</returns>
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public static bool Prefix(AudioSource __instance, double time)
|
public static bool Prefix(AudioSource __instance, double time)
|
||||||
{
|
{
|
||||||
@@ -80,9 +144,16 @@ public static class RenderEnvironmentPatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>Time.frameCount</c> (frames since startup): frameCountBase + <c>RenderManager.frameCount</c> while capturing
|
||||||
|
/// (stage >= Render) with <c>RenderContext.TimePatch</c> on.
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(Time), "frameCount", MethodType.Getter)]
|
[HarmonyPatch(typeof(Time), "frameCount", MethodType.Getter)]
|
||||||
public static class FrameCountPatch
|
public static class FrameCountPatch
|
||||||
{
|
{
|
||||||
|
/// <summary>Prefix of the getter; returns the replacement value under the conditions in the type summary.</summary>
|
||||||
|
/// <param name="__result">Replacement value.</param>
|
||||||
|
/// <returns>True to run the original getter; false when the replacement value is returned.</returns>
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
public static bool Prefix(ref int __result)
|
public static bool Prefix(ref int __result)
|
||||||
{
|
{
|
||||||
@@ -92,9 +163,13 @@ public static class RenderEnvironmentPatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary><c>Time.deltaTime</c> (seconds since the previous frame, float): 1 / RenderFPS.</summary>
|
||||||
[HarmonyPatch(typeof(Time), "deltaTime", MethodType.Getter)]
|
[HarmonyPatch(typeof(Time), "deltaTime", MethodType.Getter)]
|
||||||
public static class DeltaTimePatch
|
public static class DeltaTimePatch
|
||||||
{
|
{
|
||||||
|
/// <summary>Prefix of the getter; returns the render clock value while capturing with the time patch on.</summary>
|
||||||
|
/// <param name="__result">Replacement value in seconds.</param>
|
||||||
|
/// <returns>True to run the original getter; false when the render clock value is returned.</returns>
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
public static bool Prefix(ref float __result)
|
public static bool Prefix(ref float __result)
|
||||||
{
|
{
|
||||||
@@ -104,9 +179,13 @@ public static class RenderEnvironmentPatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary><c>Time.unscaledDeltaTime</c> (timeScale-independent frame time in seconds, float): 1 / RenderFPS.</summary>
|
||||||
[HarmonyPatch(typeof(Time), "unscaledDeltaTime", MethodType.Getter)]
|
[HarmonyPatch(typeof(Time), "unscaledDeltaTime", MethodType.Getter)]
|
||||||
public static class UnscaledDeltaTimePatch
|
public static class UnscaledDeltaTimePatch
|
||||||
{
|
{
|
||||||
|
/// <summary>Prefix of the getter; returns the render clock value while capturing with the time patch on.</summary>
|
||||||
|
/// <param name="__result">Replacement value in seconds.</param>
|
||||||
|
/// <returns>True to run the original getter; false when the render clock value is returned.</returns>
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
public static bool Prefix(ref float __result)
|
public static bool Prefix(ref float __result)
|
||||||
{
|
{
|
||||||
@@ -116,9 +195,18 @@ public static class RenderEnvironmentPatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>Screen.width</c> (window width in pixels): <c>RenderConfig.RenderWidth</c> while <see cref="RenderManager.IsActive"/>
|
||||||
|
/// (stage >= OnScene) and <c>RenderContext.ScreenPatch</c> is on (default on). Game code that sizes render textures from
|
||||||
|
/// the screen size (for example <c>scrCamera</c> recreates <c>camRT</c> at Screen.width x Screen.height when the size
|
||||||
|
/// differs) then works at the output resolution independent of the window size.
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(Screen), "width", MethodType.Getter)]
|
[HarmonyPatch(typeof(Screen), "width", MethodType.Getter)]
|
||||||
public static class ScreenWidthPatch
|
public static class ScreenWidthPatch
|
||||||
{
|
{
|
||||||
|
/// <summary>Prefix of the getter; returns the replacement value under the conditions in the type summary.</summary>
|
||||||
|
/// <param name="__result">Replacement value.</param>
|
||||||
|
/// <returns>True to run the original getter; false when the replacement value is returned.</returns>
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
public static bool Prefix(ref int __result)
|
public static bool Prefix(ref int __result)
|
||||||
{
|
{
|
||||||
@@ -128,9 +216,15 @@ public static class RenderEnvironmentPatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>Screen.height</c> (window height in pixels): <c>RenderConfig.RenderHeight</c>; same conditions as <see cref="ScreenWidthPatch"/>.
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(Screen), "height", MethodType.Getter)]
|
[HarmonyPatch(typeof(Screen), "height", MethodType.Getter)]
|
||||||
public static class ScreenHeightPatch
|
public static class ScreenHeightPatch
|
||||||
{
|
{
|
||||||
|
/// <summary>Prefix of the getter; returns the replacement value under the conditions in the type summary.</summary>
|
||||||
|
/// <param name="__result">Replacement value.</param>
|
||||||
|
/// <returns>True to run the original getter; false when the replacement value is returned.</returns>
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
public static bool Prefix(ref int __result)
|
public static bool Prefix(ref int __result)
|
||||||
{
|
{
|
||||||
@@ -140,9 +234,22 @@ public static class RenderEnvironmentPatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>AudioSource.GetSpectrumData(float[] samples, int channel, FFTWindow window)</c> (FFT magnitudes of the source's current
|
||||||
|
/// output): while capturing (stage >= Render) with <c>AdvancedConfig.ProcessAudio</c> on, fills <c>samples</c> with
|
||||||
|
/// <see cref="AudioSpectrumGenerator.GetSpectrumData"/> (spectrum of the song at the render time) for any AudioSource.
|
||||||
|
/// No AudioSource plays during capture, so the original would return silence. The game calls it from
|
||||||
|
/// <c>scrConductor</c>, <c>scrBarMaker</c> and the volume trackers for audio-reactive visuals.
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(AudioSource), "GetSpectrumData", typeof(float[]), typeof(int), typeof(FFTWindow))]
|
[HarmonyPatch(typeof(AudioSource), "GetSpectrumData", typeof(float[]), typeof(int), typeof(FFTWindow))]
|
||||||
public static class GetSpectrumDataPatch
|
public static class GetSpectrumDataPatch
|
||||||
{
|
{
|
||||||
|
/// <summary>Prefix of <c>AudioSource.GetSpectrumData</c>.</summary>
|
||||||
|
/// <param name="__instance">The AudioSource queried (not used; the song spectrum is returned for every source).</param>
|
||||||
|
/// <param name="samples">Output array; its length is the number of spectrum bins.</param>
|
||||||
|
/// <param name="channel">Channel index requested by the caller.</param>
|
||||||
|
/// <param name="window">FFT window type requested by the caller.</param>
|
||||||
|
/// <returns>True to run the original method; false when the generated spectrum was written.</returns>
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
public static bool Prefix(AudioSource __instance, float[] samples, int channel, FFTWindow window)
|
public static bool Prefix(AudioSource __instance, float[] samples, int channel, FFTWindow window)
|
||||||
{
|
{
|
||||||
@@ -152,9 +259,19 @@ public static class RenderEnvironmentPatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>AudioManager.FindOrLoadAudioClipExternal(string path, bool mp3Streaming, float length, bool stream)</c> (coroutine
|
||||||
|
/// that loads an external audio file of the level into <c>audioLib</c>): from stage Load with
|
||||||
|
/// <c>AdvancedConfig.ProcessAudio</c> on, forces <c>stream</c> to false. The game passes <c>stream</c> to
|
||||||
|
/// <c>DownloadHandlerAudioClip.streamAudio</c> for ogg/wav/aif/aiff files, so these clips are loaded as non-streaming
|
||||||
|
/// clips; the mp3 path uses <c>mp3Streaming</c> and ignores <c>stream</c>.
|
||||||
|
/// Reference: https://docs.unity3d.com/ScriptReference/Networking.DownloadHandlerAudioClip-streamAudio.html
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(AudioManager), "FindOrLoadAudioClipExternal", typeof(string), typeof(bool), typeof(float), typeof(bool))]
|
[HarmonyPatch(typeof(AudioManager), "FindOrLoadAudioClipExternal", typeof(string), typeof(bool), typeof(float), typeof(bool))]
|
||||||
public static class FindOrLoadAudioClipExternalPatch
|
public static class FindOrLoadAudioClipExternalPatch
|
||||||
{
|
{
|
||||||
|
/// <summary>Prefix of <c>AudioManager.FindOrLoadAudioClipExternal</c>; the original always runs.</summary>
|
||||||
|
/// <param name="stream">Streaming flag of the load; set to false under the conditions above.</param>
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
public static void Prefix(ref bool stream)
|
public static void Prefix(ref bool stream)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,10 +3,27 @@ using HarmonyLib;
|
|||||||
|
|
||||||
namespace ADOFAIRenderer.Patch.Game;
|
namespace ADOFAIRenderer.Patch.Game;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hides the detailed results panel (per-judgement hit counts) that the game shows on the clear screen.
|
||||||
|
/// <para>
|
||||||
|
/// Patched member: <c>DetailedResults.Show()</c>. The game calls it from <c>scrController.OnLandOnPortal</c> when a level is won
|
||||||
|
/// (and detailed results are enabled); it builds the result text per player, enables the component and activates
|
||||||
|
/// <c>scrController.detailedResults</c>.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// Active while <see cref="RenderManager.IsActive"/> (stage >= OnScene), so the panel does not appear in the rendered video
|
||||||
|
/// during the delay after the level end.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(DetailedResults))]
|
[HarmonyPatch(typeof(DetailedResults))]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public static class DetailedResultsPatch
|
public static class DetailedResultsPatch
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Prefix of <c>DetailedResults.Show</c>. While rendering, disables the component and deactivates its GameObject.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="__instance">The DetailedResults component being shown.</param>
|
||||||
|
/// <returns>True to run the original method (not rendering); false to skip it (rendering).</returns>
|
||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
[HarmonyPatch("Show")]
|
[HarmonyPatch("Show")]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
|
|||||||
@@ -4,10 +4,29 @@ using HarmonyLib;
|
|||||||
|
|
||||||
namespace ADOFAIRenderer.Patch.Game;
|
namespace ADOFAIRenderer.Patch.Game;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Suppresses the end-screen lanterns (level complete / accuracy / speed trial lamps) shown after a clear.
|
||||||
|
/// <para>
|
||||||
|
/// Patched members: <c>EndscreenLanterns.Show(scrPlayer, float, int, bool)</c>, which activates the lantern set, sets the
|
||||||
|
/// lantern texts and starts DOTween sequences (<c>chainSequence</c> for the chain drop, <c>lampSequences[3]</c> for the lamp
|
||||||
|
/// effects), returning the number of lanterns shown; and <c>EndscreenLanterns.Hide()</c>, which deactivates the GameObject.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// Active while <see cref="RenderManager.IsActive"/> (stage >= OnScene), so the lanterns and their tweens do not appear in
|
||||||
|
/// the rendered video during the delay after the level end. See also <see cref="scrUIControllerPatch"/>.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(EndscreenLanterns))]
|
[HarmonyPatch(typeof(EndscreenLanterns))]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public static class EndscreenLanternsPatch
|
public static class EndscreenLanternsPatch
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Prefix of <c>EndscreenLanterns.Show</c>. While rendering, hides the lantern set (which also runs
|
||||||
|
/// <see cref="PrefixHide"/>) and reports zero lanterns shown.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="__instance">The lantern set being shown.</param>
|
||||||
|
/// <param name="__result">Return value of Show: number of lanterns shown; set to 0 when the original is skipped.</param>
|
||||||
|
/// <returns>True to run the original method (not rendering); false to skip it (rendering).</returns>
|
||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
[HarmonyPatch("Show")]
|
[HarmonyPatch("Show")]
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
@@ -22,6 +41,21 @@ public static class EndscreenLanternsPatch
|
|||||||
return false; // Skip original Show method
|
return false; // Skip original Show method
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prefix of <c>EndscreenLanterns.Hide</c>. While rendering, releases the lantern tweens started by an earlier Show
|
||||||
|
/// before the original deactivates the GameObject.
|
||||||
|
/// <para>
|
||||||
|
/// The private fields <c>lampSequences</c> (DOTween <c>Sequence[]</c>) and <c>chainSequence</c> (<c>Sequence</c>) are read
|
||||||
|
/// through Harmony <see cref="Traverse"/>. For each sequence, a public instance method <c>Kill(bool)</c> is looked up on the
|
||||||
|
/// object's runtime type by reflection and invoked with <c>false</c> when found; then the field slot is set to null.
|
||||||
|
/// In the game's DOTween.dll, <c>Kill(this Tween, bool complete = false)</c> is an extension method of
|
||||||
|
/// <c>DG.Tweening.TweenExtensions</c>, so the lookup on <c>Sequence</c> returns null and no Kill is invoked; the effect is
|
||||||
|
/// that the fields stop referencing the sequences, while the sequences themselves keep running in DOTween.
|
||||||
|
/// Reflection failures on a single sequence (including a null array element) are ignored. The original Hide always runs
|
||||||
|
/// afterwards.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="__instance">The lantern set being hidden.</param>
|
||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
[HarmonyPatch("Hide")]
|
[HarmonyPatch("Hide")]
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
|
|||||||
@@ -5,11 +5,19 @@ using UnityEngine;
|
|||||||
namespace ADOFAIRenderer.Patch.Game;
|
namespace ADOFAIRenderer.Patch.Game;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Synchronizes mod localization with ADOFAI's in-game language settings.
|
/// Keeps the mod UI language (<see cref="LocalizationManager"/>) equal to the game language.
|
||||||
|
/// <para>
|
||||||
|
/// Patched members: <c>RDString.Setup()</c>, which initializes the game's string table and sets <c>RDString.language</c> from
|
||||||
|
/// <c>Persistence.language</c> (it returns immediately when already initialized); and <c>RDString.ChangeLanguage(SystemLanguage)</c>,
|
||||||
|
/// which stores the language in <c>Persistence.language</c>, clears the initialized flag and calls Setup again.
|
||||||
|
/// </para>
|
||||||
|
/// <para>Always active (independent of the render stage). Runs on the Unity main thread.</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(RDString))]
|
[HarmonyPatch(typeof(RDString))]
|
||||||
internal static class LanguageSyncPatch
|
internal static class LanguageSyncPatch
|
||||||
{
|
{
|
||||||
|
/// <summary>Postfix of <c>RDString.ChangeLanguage</c>: applies the selected language to the mod UI.</summary>
|
||||||
|
/// <param name="language">Language passed to ChangeLanguage.</param>
|
||||||
[HarmonyPatch(nameof(RDString.ChangeLanguage))]
|
[HarmonyPatch(nameof(RDString.ChangeLanguage))]
|
||||||
[HarmonyPostfix]
|
[HarmonyPostfix]
|
||||||
public static void OnChangeLanguage(SystemLanguage language)
|
public static void OnChangeLanguage(SystemLanguage language)
|
||||||
@@ -17,6 +25,10 @@ internal static class LanguageSyncPatch
|
|||||||
LocalizationManager.SetLanguage(language);
|
LocalizationManager.SetLanguage(language);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Postfix of <c>RDString.Setup</c>: applies <c>RDString.language</c> (the language resolved by the game) to the mod UI.
|
||||||
|
/// Runs on every Setup call, including the early-return calls made when the string table is already initialized.
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(nameof(RDString.Setup))]
|
[HarmonyPatch(nameof(RDString.Setup))]
|
||||||
[HarmonyPostfix]
|
[HarmonyPostfix]
|
||||||
public static void OnSetup()
|
public static void OnSetup()
|
||||||
|
|||||||
@@ -3,9 +3,23 @@ using HarmonyLib;
|
|||||||
|
|
||||||
namespace ADOFAIRenderer.Patch.Game;
|
namespace ADOFAIRenderer.Patch.Game;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overrides two player settings read from <c>Persistence</c> during a render: the "skip intro" option and the planet colors.
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(Persistence))]
|
[HarmonyPatch(typeof(Persistence))]
|
||||||
public static class PersistencePatch
|
public static class PersistencePatch
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Postfix of the <c>Persistence.skipIntroBehavior</c> getter (setting values Off / AfterFirstTry / Always).
|
||||||
|
/// <para>
|
||||||
|
/// <c>scrConductor.StartMusicCo</c> reads this setting when the song is scheduled and, for AfterFirstTry (after a death) or
|
||||||
|
/// Always (long intro, no checkpoint), sets <c>skipOffset</c>, which moves <c>dspTimeSong</c> back by the level offset and
|
||||||
|
/// starts the song part-way in. While <see cref="RenderManager.IsActive"/> (stage >= OnScene) and the song has not
|
||||||
|
/// started yet (<c>scrConductor.hasSongStarted</c> false), the getter returns Off, so <c>skipOffset</c> stays false and the
|
||||||
|
/// song always plays from its start. The mod's offline BGM mixing, spectrum and video background timing assume this.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="__result">Value returned by the getter; replaced with <c>SkipIntroBehavior.Off</c> under the conditions above.</param>
|
||||||
[HarmonyPostfix]
|
[HarmonyPostfix]
|
||||||
[HarmonyPatch("skipIntroBehavior", MethodType.Getter)]
|
[HarmonyPatch("skipIntroBehavior", MethodType.Getter)]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
@@ -19,6 +33,18 @@ public static class PersistencePatch
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Postfix of <c>Persistence.GetPlayerColor(bool red)</c>, which returns the player's saved planet color
|
||||||
|
/// (preset name or hex color from the general preferences; default red/blue otherwise). <c>PlanetRenderer</c> uses it to
|
||||||
|
/// color the fire (red) and ice (blue) planets.
|
||||||
|
/// <para>
|
||||||
|
/// Active from stage Load (render started, scene loading) until the stage returns to None. When the render configuration
|
||||||
|
/// sets <c>AdvancedConfig.firePlanetColor</c> (red) or <c>AdvancedConfig.icePlanetColor</c> (blue), that color replaces the
|
||||||
|
/// saved color; an unset (null) override keeps the game value. The saved preference itself is not changed.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="red">True for the fire (red) planet, false for the ice (blue) planet.</param>
|
||||||
|
/// <param name="__result">Color returned by the game; replaced by the configured override when set.</param>
|
||||||
[HarmonyPostfix]
|
[HarmonyPostfix]
|
||||||
[HarmonyPatch("GetPlayerColor")]
|
[HarmonyPatch("GetPlayerColor")]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
|
|||||||
@@ -3,9 +3,21 @@ using HarmonyLib;
|
|||||||
|
|
||||||
namespace ADOFAIRenderer.Patch.Game;
|
namespace ADOFAIRenderer.Patch.Game;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies the configured color of the third (green, "wind") planet used by multi-planet levels.
|
||||||
|
/// <para>
|
||||||
|
/// Patched members: <c>PlanetarySystem.Init()</c>, which creates the planets including <c>planetGreen</c> (default color
|
||||||
|
/// RGB 0.3, 0.7, 0.0); and <c>PlanetarySystem.ApplyMultiplanetColors()</c>, which recolors the extra planets
|
||||||
|
/// (green, yellow, purple, ...) from the red/blue planet colors (skipped by the game when <c>GCS.staticPlanetColors</c> is set).
|
||||||
|
/// Both postfixes reapply the override after the game has set its color.
|
||||||
|
/// </para>
|
||||||
|
/// <para>Active from stage Load until the stage returns to None. The fire/ice colors are handled by <see cref="PersistencePatch"/>.</para>
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(PlanetarySystem))]
|
[HarmonyPatch(typeof(PlanetarySystem))]
|
||||||
public static class PlanetarySystemPatch
|
public static class PlanetarySystemPatch
|
||||||
{
|
{
|
||||||
|
/// <summary>Postfix of <c>PlanetarySystem.Init</c>: applies the wind planet color override to the new planets.</summary>
|
||||||
|
/// <param name="__instance">The planetary system that was initialized.</param>
|
||||||
[HarmonyPostfix]
|
[HarmonyPostfix]
|
||||||
[HarmonyPatch("Init")]
|
[HarmonyPatch("Init")]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
@@ -14,6 +26,8 @@ public static class PlanetarySystemPatch
|
|||||||
ApplyWindPlanetColor(__instance);
|
ApplyWindPlanetColor(__instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Postfix of <c>PlanetarySystem.ApplyMultiplanetColors</c>: reapplies the wind planet color override.</summary>
|
||||||
|
/// <param name="__instance">The planetary system whose extra planet colors were updated.</param>
|
||||||
[HarmonyPostfix]
|
[HarmonyPostfix]
|
||||||
[HarmonyPatch("ApplyMultiplanetColors")]
|
[HarmonyPatch("ApplyMultiplanetColors")]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
@@ -22,6 +36,11 @@ public static class PlanetarySystemPatch
|
|||||||
ApplyWindPlanetColor(__instance);
|
ApplyWindPlanetColor(__instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets <c>planetGreen</c>'s renderer color to <c>AdvancedConfig.windPlanetColor</c> when a render is in progress
|
||||||
|
/// (stage != None) and the override is set. Does nothing when the override is null or the planet does not exist.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="planetarySystem">The planetary system to update.</param>
|
||||||
private static void ApplyWindPlanetColor(PlanetarySystem planetarySystem)
|
private static void ApplyWindPlanetColor(PlanetarySystem planetarySystem)
|
||||||
{
|
{
|
||||||
if (RenderManager.Instance == null || RenderManager.Instance.stage == RenderStage.None) return;
|
if (RenderManager.Instance == null || RenderManager.Instance.stage == RenderStage.None) return;
|
||||||
|
|||||||
@@ -6,10 +6,28 @@ using UnityEngine.UI;
|
|||||||
|
|
||||||
namespace ADOFAIRenderer.Patch.Game;
|
namespace ADOFAIRenderer.Patch.Game;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the "Render" button to the level editor toolbar.
|
||||||
|
/// <para>
|
||||||
|
/// Patched member: <c>scnEditor.Start()</c>, the editor scene setup (it wires the toolbar buttons, including
|
||||||
|
/// <c>buttonExit</c>). The postfix clones the exit button next to it and makes the clone start a render.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// Always active except when the mod features are disabled (<see cref="RenderMod.IsFeatureDisabled"/>, set from the
|
||||||
|
/// driver warning dialog). Runs on the Unity main thread each time the editor scene starts.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(scnEditor))]
|
[HarmonyPatch(typeof(scnEditor))]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public static class scnEditorPatch
|
public static class scnEditorPatch
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Postfix of <c>scnEditor.Start</c>: instantiates a copy of <c>buttonExit</c> under the same parent, named
|
||||||
|
/// <c>buttonRender</c>, whose click handler is <see cref="RenderManager.StartRender"/>, and replaces its icon with the
|
||||||
|
/// editor's MoveCamera event sprite (24 x 24 units). Logs a message and adds nothing when the editor or exit button is missing.
|
||||||
|
/// When the icon child or the sprite is not found, the button keeps the exit icon.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="__instance">The editor scene component.</param>
|
||||||
[HarmonyPostfix]
|
[HarmonyPostfix]
|
||||||
[HarmonyPatch("Start")]
|
[HarmonyPatch("Start")]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
@@ -38,8 +56,9 @@ public static class scnEditorPatch
|
|||||||
var images = buttonObj.GetComponentsInChildren<Image>();
|
var images = buttonObj.GetComponentsInChildren<Image>();
|
||||||
if (images.Length <= 1) return;
|
if (images.Length <= 1) return;
|
||||||
|
|
||||||
|
// images[0] is the background on the root, images[1] is the icon on the child.
|
||||||
|
// Sprite path: the icon of the editor's MoveCamera level event, loaded from the game's Resources.
|
||||||
// ReSharper disable once Unity.UnknownResource
|
// ReSharper disable once Unity.UnknownResource
|
||||||
// images[0] is the background on the root, images[1] is the icon on the child
|
|
||||||
var sprite = Resources.Load<Sprite>("LevelEditor/LevelEvents/MoveCamera");
|
var sprite = Resources.Load<Sprite>("LevelEditor/LevelEvents/MoveCamera");
|
||||||
if (sprite == null) return;
|
if (sprite == null) return;
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,26 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace ADOFAIRenderer.Patch.Game;
|
namespace ADOFAIRenderer.Patch.Game;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Patches of <c>scnGame</c> (custom level scene controller, also used by the editor's play mode): hides tiles and planets
|
||||||
|
/// for the "non-tile" render option and replaces the background video player during a render.
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(scnGame))]
|
[HarmonyPatch(typeof(scnGame))]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public static class scnGamePatch
|
public static class scnGamePatch
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Postfix of <c>scnGame.Play(int seqID, bool remakeFloors)</c>, which starts (or restarts) play of the custom level:
|
||||||
|
/// it resets the conductor, camera and controller state for the given start tile.
|
||||||
|
/// <para>
|
||||||
|
/// Active from stage Load when <c>AdvancedConfig.NonTile</c> is enabled. Disables the renderers of every floor in
|
||||||
|
/// <c>scrLevelMaker.listFloors</c> (floor mesh, legacy sprite, icon, outline, top/bottom glow, multi-planet line,
|
||||||
|
/// hold mesh, dummy planets) and of every planet of every player (sprite mesh, core/tail/sparks particles, ring, glow,
|
||||||
|
/// face, samurai sprite), so only the background and decorations remain in the rendered video. The renderers are only
|
||||||
|
/// disabled; the objects keep running. Hold meshes re-enable themselves, so <c>RenderManager.OnLastUpdate</c> disables
|
||||||
|
/// them again every frame.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
[HarmonyPostfix]
|
[HarmonyPostfix]
|
||||||
[HarmonyPatch("Play")]
|
[HarmonyPatch("Play")]
|
||||||
public static void ProcessFloors()
|
public static void ProcessFloors()
|
||||||
@@ -47,6 +63,7 @@ public static class scnGamePatch
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Disables every renderer that draws one planet (fire, ice, extra planets and floor dummy planets).
|
||||||
void DisablePlanetRenderer(PlanetRenderer planet)
|
void DisablePlanetRenderer(PlanetRenderer planet)
|
||||||
{
|
{
|
||||||
if (planet.sprite) DisableRenderer(planet.sprite.meshRenderer);
|
if (planet.sprite) DisableRenderer(planet.sprite.meshRenderer);
|
||||||
@@ -62,12 +79,29 @@ public static class scnGamePatch
|
|||||||
foreach (var renderer in renderers) DisableRenderer(renderer);
|
foreach (var renderer in renderers) DisableRenderer(renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets Renderer.enabled = false; null or destroyed renderers are skipped (Unity null check).
|
||||||
void DisableRenderer(Renderer? renderer)
|
void DisableRenderer(Renderer? renderer)
|
||||||
{
|
{
|
||||||
if (renderer) renderer.enabled = false;
|
if (renderer) renderer.enabled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prefix of <c>scnGame.UpdateVideo()</c>, which sets up the level's background video on the native Unity
|
||||||
|
/// <c>VideoPlayer</c> (<c>scnGame.videoBG</c>): it reads <c>miscSettings.bgVideo</c>, loads a <c>VideoClip</c> for internal
|
||||||
|
/// (Resources / Addressables) and bundle levels or sets <c>videoBG.url</c> to the file next to the custom level, then calls
|
||||||
|
/// <c>Stop()</c>, <c>Prepare()</c> and sets <c>isLooping</c> from <c>loopVideo</c>. The game calls it from
|
||||||
|
/// <c>scnGame.FinishCustomLevelLoading</c> and from <c>scnGame.ReloadAssets</c> when <c>force</c> is true.
|
||||||
|
/// <para>
|
||||||
|
/// While <see cref="RenderManager.IsActive"/> (stage >= OnScene), an external video file of a custom level is played by
|
||||||
|
/// <see cref="VideoBGPlayer"/> (FFmpeg decoder), which shows the frame for the render time of each frame. The native
|
||||||
|
/// <c>VideoPlayer</c> plays in real time and would drift from the offline render clock, so it is disabled for that case.
|
||||||
|
/// Internal and bundle levels (built-in <c>VideoClip</c> assets, not files) and a missing file use the original method.
|
||||||
|
/// </para>
|
||||||
|
/// <para>Outside a render, any <see cref="VideoBGPlayer"/> left on the video object is destroyed and the native player re-enabled.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="__instance">The scene controller.</param>
|
||||||
|
/// <returns>True to run the original UpdateVideo; false when the mod handled the video (or there is none).</returns>
|
||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
[HarmonyPatch("UpdateVideo")]
|
[HarmonyPatch("UpdateVideo")]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
@@ -109,7 +143,8 @@ public static class scnGamePatch
|
|||||||
return true; // Let the original print error
|
return true; // Let the original print error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable native VideoPlayer component (to prevent it from attempting real-time playback and sync errors)
|
// Camera for the CameraFarPlane render mode: the VideoPlayer's target camera, or the "BGStaticCam" scene object
|
||||||
|
// when none is assigned.
|
||||||
var targetCamera = __instance.videoBG.targetCamera;
|
var targetCamera = __instance.videoBG.targetCamera;
|
||||||
if (!targetCamera)
|
if (!targetCamera)
|
||||||
{
|
{
|
||||||
@@ -120,6 +155,7 @@ public static class scnGamePatch
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disable the native VideoPlayer so it does not play the video in real time alongside VideoBGPlayer.
|
||||||
__instance.videoBG.enabled = false;
|
__instance.videoBG.enabled = false;
|
||||||
|
|
||||||
// Add or retrieve our custom FFmpeg video background player
|
// Add or retrieve our custom FFmpeg video background player
|
||||||
|
|||||||
@@ -3,16 +3,37 @@ using HarmonyLib;
|
|||||||
|
|
||||||
namespace ADOFAIRenderer.Patch.Game;
|
namespace ADOFAIRenderer.Patch.Game;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Protects the camera render texture <c>scrCamera.camRT</c> when the custom frame rate effect is turned off while it is
|
||||||
|
/// already off.
|
||||||
|
/// <para>
|
||||||
|
/// Patched member: <c>scrCamera.SetCustomFrameRate(bool enable, float frame)</c> (level effect that updates the screen at a
|
||||||
|
/// lower frame rate). Enabling assigns a new RenderTexture to the camera quad material and refreshes it at <c>frame</c> fps.
|
||||||
|
/// Disabling calls <c>Release()</c> on the quad material's current <c>mainTexture</c> and assigns <c>camRT</c> back. When
|
||||||
|
/// the effect was never enabled, <c>mainTexture</c> is <c>camRT</c> itself, so the original releases the live camera
|
||||||
|
/// render texture.
|
||||||
|
/// </para>
|
||||||
|
/// <para>Always active (independent of the render stage).</para>
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(scrCamera))]
|
[HarmonyPatch(typeof(scrCamera))]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public class scrCameraPatch
|
public class scrCameraPatch
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Prefix of <c>scrCamera.SetCustomFrameRate</c>. For a disable request while <c>enableCustomFPS</c> is already false,
|
||||||
|
/// stores the arguments in <c>enableCustomFPS</c> / <c>frameRate</c> and skips the original (no texture is released).
|
||||||
|
/// Enable requests and disable requests while the effect is on run the original.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="__instance">The camera.</param>
|
||||||
|
/// <param name="enable">True to turn the custom frame rate on, false to turn it off.</param>
|
||||||
|
/// <param name="frame">Custom frame rate in frames per second.</param>
|
||||||
|
/// <returns>True to run the original method; false to skip it.</returns>
|
||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
[HarmonyPatch("SetCustomFrameRate")]
|
[HarmonyPatch("SetCustomFrameRate")]
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
private static bool FixCamRT(scrCamera __instance, bool enable, float frame)
|
private static bool FixCamRT(scrCamera __instance, bool enable, float frame)
|
||||||
{
|
{
|
||||||
// There is an issue that `mainTexture.Release` triggers even if `mainTexture` is `camRT`, so mod tries to fix it
|
// Disable request while the effect is off: the quad material's mainTexture is camRT, which the original would Release().
|
||||||
if (enable || __instance.enableCustomFPS != enable) return true;
|
if (enable || __instance.enableCustomFPS != enable) return true;
|
||||||
|
|
||||||
__instance.enableCustomFPS = enable;
|
__instance.enableCustomFPS = enable;
|
||||||
|
|||||||
@@ -3,10 +3,25 @@ using HarmonyLib;
|
|||||||
|
|
||||||
namespace ADOFAIRenderer.Patch.Game;
|
namespace ADOFAIRenderer.Patch.Game;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Keeps the game controller (<c>scrController</c>) in uninterrupted play during a render: no pause, no failure,
|
||||||
|
/// no scene change after the clear, and no clear texts.
|
||||||
|
/// <para>
|
||||||
|
/// All patches are active while <see cref="RenderManager.IsActive"/> (stage >= OnScene). RenderManager also sets
|
||||||
|
/// <c>noFail</c>, <c>noFailInfiniteMargin</c> and <c>disableCongratsMessage</c> on the controller when the scene is ready.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(scrController))]
|
[HarmonyPatch(typeof(scrController))]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public static class scrControllerPatch
|
public static class scrControllerPatch
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Prefix of <c>scrController.TogglePauseGame()</c> (pause menu toggle; sets <c>Time.timeScale</c> to 0 or 1 and shows the
|
||||||
|
/// pause menu; also called from the pause button, the pause key and <c>OnApplicationPauseCallback</c>). Skipped while
|
||||||
|
/// rendering, so the level cannot be paused. The skipped call returns
|
||||||
|
/// false (Harmony default for the unset bool result).
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True to run the original method (not rendering); false to skip it (rendering).</returns>
|
||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
[HarmonyPatch("TogglePauseGame")]
|
[HarmonyPatch("TogglePauseGame")]
|
||||||
public static bool PreventPause()
|
public static bool PreventPause()
|
||||||
@@ -14,6 +29,12 @@ public static class scrControllerPatch
|
|||||||
return !RenderManager.IsActive;
|
return !RenderManager.IsActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prefix of <c>scrController.PortalTravelAction(Portal)</c>, 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.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True to run the original method (not rendering); false to skip it (rendering).</returns>
|
||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
[HarmonyPatch("PortalTravelAction")]
|
[HarmonyPatch("PortalTravelAction")]
|
||||||
public static bool PreventPortalTravel()
|
public static bool PreventPortalTravel()
|
||||||
@@ -21,6 +42,11 @@ public static class scrControllerPatch
|
|||||||
return !RenderManager.IsActive;
|
return !RenderManager.IsActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prefix of <c>scrController.FailAction(...)</c>, which enters the Fail state: stops the song and all sounds, pauses the
|
||||||
|
/// VFX video and shows the failure texts. Skipped while rendering.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True to run the original method (not rendering); false to skip it (rendering).</returns>
|
||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
[HarmonyPatch("FailAction")]
|
[HarmonyPatch("FailAction")]
|
||||||
public static bool PreventFailAction()
|
public static bool PreventFailAction()
|
||||||
@@ -28,6 +54,11 @@ public static class scrControllerPatch
|
|||||||
return !RenderManager.IsActive;
|
return !RenderManager.IsActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prefix of <c>scrController.Fail2Action()</c>, which moves from Fail to Fail2 (accuracy calculation, saving of the
|
||||||
|
/// best result for boss/custom boss levels, death counter). Skipped while rendering.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True to run the original method (not rendering); false to skip it (rendering).</returns>
|
||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
[HarmonyPatch("Fail2Action")]
|
[HarmonyPatch("Fail2Action")]
|
||||||
public static bool PreventFail2Action()
|
public static bool PreventFail2Action()
|
||||||
@@ -35,6 +66,11 @@ public static class scrControllerPatch
|
|||||||
return !RenderManager.IsActive;
|
return !RenderManager.IsActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prefix of <c>scrController.OnPlayerDied(...)</c>, which kills the remaining players and calls FailAction when no other
|
||||||
|
/// player is alive. Skipped while rendering.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True to run the original method (not rendering); false to skip it (rendering).</returns>
|
||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
[HarmonyPatch("OnPlayerDied")]
|
[HarmonyPatch("OnPlayerDied")]
|
||||||
public static bool PreventPlayerDied()
|
public static bool PreventPlayerDied()
|
||||||
@@ -42,6 +78,17 @@ public static class scrControllerPatch
|
|||||||
return !RenderManager.IsActive;
|
return !RenderManager.IsActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Postfix of <c>scrController.OnLandOnPortal(...)</c>, the win handler on the last tile: it sets and activates
|
||||||
|
/// <c>txtCongrats</c> / <c>txtAprilCongrats</c> / <c>txtAllStrictClear</c>, may show the detailed results and end-screen
|
||||||
|
/// lanterns, saves the result and changes the state to Won.
|
||||||
|
/// <para>
|
||||||
|
/// 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 (<c>RenderContext.EndCondition</c>) waits for it.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="__instance">The game controller.</param>
|
||||||
[HarmonyPostfix]
|
[HarmonyPostfix]
|
||||||
[HarmonyPatch("OnLandOnPortal")]
|
[HarmonyPatch("OnLandOnPortal")]
|
||||||
public static void PreventWinTextsPostfix(scrController __instance)
|
public static void PreventWinTextsPostfix(scrController __instance)
|
||||||
|
|||||||
@@ -3,10 +3,25 @@ using HarmonyLib;
|
|||||||
|
|
||||||
namespace ADOFAIRenderer.Patch.Game;
|
namespace ADOFAIRenderer.Patch.Game;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prevents the end portal from being spawned on level tiles for the "non-tile" render option.
|
||||||
|
/// <para>
|
||||||
|
/// Patched member: <c>scrFloor.SpawnPortalParticles()</c>, which instantiates the animated portal prefab
|
||||||
|
/// (<c>scrPortalParticles</c>: glow, particles, cap and icon) as a child of the floor. The game calls it from
|
||||||
|
/// <c>scrLevelMaker</c>, <c>scrController</c> and <c>scrFloor</c> for the portal floor.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// Active from stage Load when <c>AdvancedConfig.NonTile</c> is enabled, and only for floors that belong to
|
||||||
|
/// <c>scrLevelMaker.listFloors</c> (the level's tiles). See <see cref="scnGamePatch.ProcessFloors"/> for the other tile renderers.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(scrFloor))]
|
[HarmonyPatch(typeof(scrFloor))]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public class scrFloorPatch
|
public class scrFloorPatch
|
||||||
{
|
{
|
||||||
|
/// <summary>Prefix of <c>scrFloor.SpawnPortalParticles</c>.</summary>
|
||||||
|
/// <param name="__instance">The floor that would receive the portal.</param>
|
||||||
|
/// <returns>True to run the original method (spawn the portal); false to skip it (non-tile render, level floor).</returns>
|
||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
[HarmonyPatch("SpawnPortalParticles")]
|
[HarmonyPatch("SpawnPortalParticles")]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
|
|||||||
@@ -3,10 +3,20 @@ using HarmonyLib;
|
|||||||
|
|
||||||
namespace ADOFAIRenderer.Patch.Game;
|
namespace ADOFAIRenderer.Patch.Game;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hides the judgement texts (e.g. Perfect, Early, Late) during a render unless the render configuration enables them.
|
||||||
|
/// <para>
|
||||||
|
/// Patched member: <c>scrHitTextManager.ShowHitText(HitMargin, scrPlanet, float, int?)</c>, which shows the hit margin
|
||||||
|
/// text at the planet for each hit (the game itself skips it for no-HUD modes and hidden margins).
|
||||||
|
/// </para>
|
||||||
|
/// <para>Active while <see cref="RenderManager.IsActive"/> (stage >= OnScene) and <c>RenderConfig.ShowJudgementText</c> is false.</para>
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(scrHitTextManager), "ShowHitText")]
|
[HarmonyPatch(typeof(scrHitTextManager), "ShowHitText")]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public class scrHitTextManagerPatch
|
public class scrHitTextManagerPatch
|
||||||
{
|
{
|
||||||
|
/// <summary>Prefix of <c>scrHitTextManager.ShowHitText</c>.</summary>
|
||||||
|
/// <returns>True to run the original method (not rendering, or judgement texts enabled); false to skip it.</returns>
|
||||||
public static bool Prefix()
|
public static bool Prefix()
|
||||||
{
|
{
|
||||||
return !RenderManager.IsActive || RenderManager.Instance is { Configuration: { ShowJudgementText: true } };
|
return !RenderManager.IsActive || RenderManager.Instance is { Configuration: { ShowJudgementText: true } };
|
||||||
|
|||||||
@@ -3,10 +3,25 @@ using HarmonyLib;
|
|||||||
|
|
||||||
namespace ADOFAIRenderer.Patch.Game;
|
namespace ADOFAIRenderer.Patch.Game;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Keeps a render from writing custom level results to the player's save data.
|
||||||
|
/// <para>
|
||||||
|
/// Patched member: <c>scrMistakesManager.SaveCustom(string hash, bool wonLevel, float multiplier)</c>, which compares the
|
||||||
|
/// run with the stored completion, accuracy, speed trial and death records of the custom level (keyed by the level hash),
|
||||||
|
/// stores new bests through <c>Persistence</c> and returns an <c>EndLevelInfo</c>. The game calls it from
|
||||||
|
/// <c>scrController.OnLandOnPortal</c> (custom level win) and <c>Fail2Action</c> (custom boss level failure).
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// Active while <see cref="RenderManager.IsActive"/> (stage >= OnScene); the run of a render is not stored as a player
|
||||||
|
/// record. The skipped call returns a default <c>EndLevelInfo</c>.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(scrMistakesManager))]
|
[HarmonyPatch(typeof(scrMistakesManager))]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public static class scrMistakesManagerPatch
|
public static class scrMistakesManagerPatch
|
||||||
{
|
{
|
||||||
|
/// <summary>Prefix of <c>scrMistakesManager.SaveCustom</c>.</summary>
|
||||||
|
/// <returns>True to run the original method (not rendering); false to skip it (rendering).</returns>
|
||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
[HarmonyPatch("SaveCustom")]
|
[HarmonyPatch("SaveCustom")]
|
||||||
public static bool PreventSaveCustom()
|
public static bool PreventSaveCustom()
|
||||||
|
|||||||
@@ -3,10 +3,27 @@ using HarmonyLib;
|
|||||||
|
|
||||||
namespace ADOFAIRenderer.Patch.Game;
|
namespace ADOFAIRenderer.Patch.Game;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces the "any valid input" check with a render-controlled value, so player input cannot affect the level during a render.
|
||||||
|
/// <para>
|
||||||
|
/// Patched member: <c>scrPlayerManager.AnyValidInputWasTriggered()</c>, true when any player pressed a valid key this frame.
|
||||||
|
/// <c>scrController</c> uses it to leave the press-to-start wait, to restart after a failure (<c>Fail2_Update</c>) and to
|
||||||
|
/// leave the level after a win (<c>Won_Update</c>).
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// Active while <see cref="RenderManager.IsActive"/> (stage >= OnScene). The result is true only while
|
||||||
|
/// <c>RenderManager.countdown</c> is 0 and <c>RenderManager.frameCount</c> is 0, that is, after the 3 second countdown in
|
||||||
|
/// <c>SetupPlay</c> has ended and reset the frame counter and before the next frame increments it; it is false otherwise.
|
||||||
|
/// <c>SetupPlay</c> also sets <c>scrController.levelWasSkipped</c>, which ends the press-to-start wait independently of input.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(scrPlayerManager))]
|
[HarmonyPatch(typeof(scrPlayerManager))]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public static class scrPlayerManagerPatch
|
public static class scrPlayerManagerPatch
|
||||||
{
|
{
|
||||||
|
/// <summary>Prefix of <c>scrPlayerManager.AnyValidInputWasTriggered</c>.</summary>
|
||||||
|
/// <param name="__result">Return value used when the original is skipped (see class summary).</param>
|
||||||
|
/// <returns>True to run the original method (not rendering); false to skip it (rendering).</returns>
|
||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
[HarmonyPatch("AnyValidInputWasTriggered")]
|
[HarmonyPatch("AnyValidInputWasTriggered")]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
|
|||||||
@@ -4,10 +4,26 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace ADOFAIRenderer.Patch.Game;
|
namespace ADOFAIRenderer.Patch.Game;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Makes the end portal particle systems run on scaled game time during a render.
|
||||||
|
/// <para>
|
||||||
|
/// Patched member: <c>scrPortalParticles.Start()</c> (the animated portal spawned by <c>scrFloor.SpawnPortalParticles</c>;
|
||||||
|
/// Start pre-simulates its particle system for 1 second and plays it).
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// Active from stage Load. RenderManager sets <c>Time.captureDeltaTime</c> to 1 / RenderFPS, which advances scaled time by a
|
||||||
|
/// fixed step per frame; unscaled time is not affected by captureDeltaTime and follows real time. A particle system with
|
||||||
|
/// <c>main.useUnscaledTime</c> would therefore play at the real (slower or faster) frame rate of the offline render, so the
|
||||||
|
/// postfix sets it to false on every particle system under the portal (including inactive children).
|
||||||
|
/// Reference: https://docs.unity3d.com/ScriptReference/Time-captureDeltaTime.html
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(scrPortalParticles))]
|
[HarmonyPatch(typeof(scrPortalParticles))]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public static class scrPortalParticlesPatch
|
public static class scrPortalParticlesPatch
|
||||||
{
|
{
|
||||||
|
/// <summary>Postfix of <c>scrPortalParticles.Start</c>: sets <c>useUnscaledTime = false</c> on all child particle systems.</summary>
|
||||||
|
/// <param name="__instance">The portal that started.</param>
|
||||||
[HarmonyPostfix]
|
[HarmonyPostfix]
|
||||||
[HarmonyPatch("Start")]
|
[HarmonyPatch("Start")]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
|
|||||||
@@ -3,10 +3,26 @@ using HarmonyLib;
|
|||||||
|
|
||||||
namespace ADOFAIRenderer.Patch.Game;
|
namespace ADOFAIRenderer.Patch.Game;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hides the "press any key to begin" text of the first level start of a render.
|
||||||
|
/// <para>
|
||||||
|
/// Patched member: <c>scrPressToStart.ShowText()</c>, which sets the press/tap-to-begin text and shows the difficulty
|
||||||
|
/// selector (<c>scrUIController.ShowDifficultyContainer</c>). <c>scrController</c> calls it before waiting for the start input.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// Active once per render: <c>RenderManager.skipPressToStart</c> is set when the render loads <c>scnGame</c> and cleared by
|
||||||
|
/// the first ShowText call (or when the render stops). Later ShowText calls run the original.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(scrPressToStart))]
|
[HarmonyPatch(typeof(scrPressToStart))]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public class scrPressToStartPatch
|
public class scrPressToStartPatch
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Prefix of <c>scrPressToStart.ShowText</c>. When <c>skipPressToStart</c> is set, clears it, hides the difficulty
|
||||||
|
/// selector and skips the original.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True to run the original method; false to skip it (first ShowText of a render).</returns>
|
||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
[HarmonyPatch("ShowText")]
|
[HarmonyPatch("ShowText")]
|
||||||
private static bool PreventShowText()
|
private static bool PreventShowText()
|
||||||
|
|||||||
@@ -6,10 +6,31 @@ using UnityEngine.UI;
|
|||||||
|
|
||||||
namespace ADOFAIRenderer.Patch.Game;
|
namespace ADOFAIRenderer.Patch.Game;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces the game's status text (normally "Autoplay", "Auto tile" or "Debug Mode") with the render status.
|
||||||
|
/// <para>
|
||||||
|
/// Patched member: <c>scrShowIfDebug.Update()</c>, which each frame sets its UI <c>Text</c> (<c>txt</c>) to the autoplay /
|
||||||
|
/// auto-tile / debug label or hides it (no-HUD modes, recording mode), in <c>defaultColor</c> (green) or orange for auto tiles.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// Active while <see cref="RenderManager.IsActive"/> (stage >= OnScene). The postfix overwrites whatever the original set,
|
||||||
|
/// every frame, so the text is shown even in the game's no-HUD modes.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(scrShowIfDebug))]
|
[HarmonyPatch(typeof(scrShowIfDebug))]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public static class scrShowIfDebugPatch
|
public static class scrShowIfDebugPatch
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Postfix of <c>scrShowIfDebug.Update</c>. Shows, in the default (green) color:
|
||||||
|
/// <list type="bullet">
|
||||||
|
/// <item>"Render in Ns.." during the countdown (N = <c>RenderManager.countdown</c>, seconds), otherwise "Render";</item>
|
||||||
|
/// <item>after the end condition is met (<c>endFrame</c> != -1), a second line "Waiting for X.Xs.." with the remaining
|
||||||
|
/// part of <c>DelayAfterEnd</c> in render seconds ((frameCount - endFrame) / RenderFPS elapsed), clamped at 0.</item>
|
||||||
|
/// </list>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="___txt">Private field <c>txt</c>: the status Text component.</param>
|
||||||
|
/// <param name="___defaultColor">Private field <c>defaultColor</c>: the game's status text color.</param>
|
||||||
[HarmonyPostfix]
|
[HarmonyPostfix]
|
||||||
[HarmonyPatch("Update")]
|
[HarmonyPatch("Update")]
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
|
|||||||
@@ -3,10 +3,26 @@ using HarmonyLib;
|
|||||||
|
|
||||||
namespace ADOFAIRenderer.Patch.Game;
|
namespace ADOFAIRenderer.Patch.Game;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hides the end-screen lanterns again right after the game shows them.
|
||||||
|
/// <para>
|
||||||
|
/// Patched member: <c>scrUIController.ShowEndscreenLanterns()</c>, which calls <c>EndscreenLanterns.Show</c> for each player's
|
||||||
|
/// lantern set (and fades the end-screen flash in co-op). <c>scrController.OnLandOnPortal</c> calls it on a win.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// Active while <see cref="RenderManager.IsActive"/> (stage >= OnScene). Works together with <see cref="EndscreenLanternsPatch"/>,
|
||||||
|
/// which already skips <c>EndscreenLanterns.Show</c> during a render.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
[HarmonyPatch(typeof(scrUIController))]
|
[HarmonyPatch(typeof(scrUIController))]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public class scrUIControllerPatch
|
public class scrUIControllerPatch
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Postfix of <c>scrUIController.ShowEndscreenLanterns</c>: calls <c>HideEndscreenLanterns()</c>, which runs
|
||||||
|
/// <c>EndscreenLanterns.Hide</c> on every lantern set.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="__instance">The UI controller.</param>
|
||||||
[HarmonyPostfix]
|
[HarmonyPostfix]
|
||||||
[HarmonyPatch("ShowEndscreenLanterns")]
|
[HarmonyPatch("ShowEndscreenLanterns")]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
|
|||||||
Reference in new issue
Block a user