SHA256
106 lines
4.8 KiB
C#
106 lines
4.8 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using ADOFAIRenderer.Core;
|
|
using HarmonyLib;
|
|
|
|
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))]
|
|
// ReSharper disable once InconsistentNaming
|
|
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]
|
|
[HarmonyPatch("Show")]
|
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
|
public static bool ForceHide(EndscreenLanterns __instance, ref int __result)
|
|
{
|
|
if (!RenderManager.IsActive) return true;
|
|
if (__instance != null)
|
|
{
|
|
__instance.Hide();
|
|
}
|
|
__result = 0; // Return 0 lanterns shown
|
|
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]
|
|
[HarmonyPatch("Hide")]
|
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
|
public static void PrefixHide(EndscreenLanterns __instance)
|
|
{
|
|
if (__instance == null) return;
|
|
if (!RenderManager.IsActive) return;
|
|
|
|
var traverse = Traverse.Create(__instance);
|
|
|
|
// Kill lampSequences (private Sequence[] lampSequences) via reflection
|
|
var lampSequencesField = traverse.Field("lampSequences");
|
|
var lampSequencesObj = lampSequencesField.GetValue();
|
|
if (lampSequencesObj is System.Array lampSequencesArray)
|
|
{
|
|
for (var i = 0; i < lampSequencesArray.Length; i++)
|
|
{
|
|
var item = lampSequencesArray.GetValue(i);
|
|
try
|
|
{
|
|
var killMethod = item.GetType().GetMethod("Kill", new[] { typeof(bool) });
|
|
killMethod?.Invoke(item, new object[] { false });
|
|
}
|
|
catch
|
|
{
|
|
// Ignore reflection errors
|
|
}
|
|
lampSequencesArray.SetValue(null, i);
|
|
}
|
|
}
|
|
|
|
// Kill chainSequence (private Sequence chainSequence) via reflection
|
|
var chainSequenceField = traverse.Field("chainSequence");
|
|
var chainSequenceObj = chainSequenceField.GetValue();
|
|
if (chainSequenceObj == null) return;
|
|
|
|
try
|
|
{
|
|
var killMethod = chainSequenceObj.GetType().GetMethod("Kill", new[] { typeof(bool) });
|
|
killMethod?.Invoke(chainSequenceObj, new object[] { false });
|
|
}
|
|
catch
|
|
{
|
|
// Ignore reflection errors
|
|
}
|
|
chainSequenceField.SetValue(null);
|
|
}
|
|
} |