using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Cysharp.Threading.Tasks;
using Dialogue;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using NineSolsAPI;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("CutsceneSkip")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("A Nine Sols mod that enables skipping any cutscene or dialogue (that won't break the game).")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+f2ef21ec8b14004ebe0e3cc19e8b497315bd320b")]
[assembly: AssemblyProduct("CutsceneSkip")]
[assembly: AssemblyTitle("CutsceneSkip")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace CutsceneSkip
{
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("CutsceneSkip", "CutsceneSkip", "1.0.0")]
public class CutsceneSkip : BaseUnityPlugin
{
private static ConfigEntry<KeyboardShortcut> skipKeybind = null;
private Harmony harmony;
public static (A2_SG4_Logic?, string) activeA2SG4 = (null, "");
public static (A4_S5_Logic?, string) activeA4S5 = (null, "");
public static string dialogueSkipNotificationId = "";
public static (SimpleCutsceneManager?, string) activeCutscene = (null, "");
public static (VideoPlayAction?, string) activeVideo = (null, "");
public static string KuafuEndingChoiceCutsceneGOPath = "AG_S2/Room/NPCs/SimpleCutSceneFSM_結尾/FSM Animator/LogicRoot/[CutScene]";
public static string SkipKeybindText()
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
KeyboardShortcut value = skipKeybind.Value;
return ((KeyboardShortcut)(ref value)).Serialize();
}
private void Awake()
{
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
Log.Init(((BaseUnityPlugin)this).Logger);
RCGLifeCycle.DontDestroyForever(((Component)this).gameObject);
harmony = Harmony.CreateAndPatchAll(typeof(CutsceneSkip).Assembly, (string)null);
skipKeybind = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("", "Skip Keybind", new KeyboardShortcut((KeyCode)107, (KeyCode[])(object)new KeyCode[1] { (KeyCode)306 }), "The keyboard shortcut to actually skip cutscenes and dialogue.");
KeybindManager.Add((MonoBehaviour)(object)this, (Action)SkipActiveCutsceneOrDialogue, (Func<KeyboardShortcut>)(() => skipKeybind.Value));
((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin CutsceneSkip is loaded!");
Notifications.Awake();
}
public static bool KuafuEndingChoiceCutsceneActive()
{
if ((Object)(object)activeCutscene.Item1 == (Object)null)
{
return false;
}
GameObject val = GameObject.Find(KuafuEndingChoiceCutsceneGOPath);
return (Object)(object)((Component)activeCutscene.Item1).gameObject == (Object)(object)val;
}
private void SkipActiveCutsceneOrDialogue()
{
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
//IL_0063: Unknown result type (might be due to invalid IL or missing references)
if (KuafuEndingChoiceCutsceneActive())
{
Log.Info("Not allowing the player to skip anything because we're in the Kuafu ending choice conversation, where even dialogue skipping softlocks.");
return;
}
GameObject obj = GameObject.Find("GameCore(Clone)/RCG LifeCycle/UIManager/GameplayUICamera/Always Canvas/DialoguePlayer(KeepThisEnable)");
DialoguePlayer val = ((obj != null) ? obj.GetComponent<DialoguePlayer>() : null);
if ((Object)(object)activeA2SG4.Item1 != (Object)null)
{
A2_SG4_Logic item = activeA2SG4.Item1;
if ((Object)(object)val != (Object)null)
{
AnimatorStateInfo currentAnimatorStateInfo = val.phoneUI.phoneRingAnimator.GetCurrentAnimatorStateInfo(0);
if (((AnimatorStateInfo)(ref currentAnimatorStateInfo)).IsName("Webcam_Show"))
{
Log.Info("Found A2_SG4_Logic a.k.a. Heng Power Reservoir flashback, but doing nothing because the phone UI is currently ringing. If we skip now that ringing will go on forever.");
return;
}
}
bool num = AccessTools.FieldRefAccess<A2_SG4_Logic, bool>("_done").Invoke(item);
if (num)
{
Log.Info("A2_SG4_Logic's _done flag is already set. Player must have skipped too early. Resetting _done flag back to false.");
AccessTools.FieldRefAccess<A2_SG4_Logic, bool>("_done").Invoke(item) = false;
if (AccessTools.FieldRefAccess<SceneConnectionPoint, bool>("touchedChangeSceneTrigger").Invoke(item.connectionToS2))
{
Log.Info("A2_SG4_Logic::connectionToS2's touchedChangeSceneTrigger flag is also already set. Also resetting that touchedChangeSceneTrigger flag back to false.");
AccessTools.FieldRefAccess<SceneConnectionPoint, bool>("touchedChangeSceneTrigger").Invoke(item.connectionToS2) = false;
}
if (AccessTools.FieldRefAccess<SceneConnectionPoint, bool>("touchedChangeSceneTrigger").Invoke(item.connectionToS3))
{
Log.Info("A2_SG4_Logic::connectionToS3's touchedChangeSceneTrigger flag is also already set. Also resetting that touchedChangeSceneTrigger flag back to false.");
AccessTools.FieldRefAccess<SceneConnectionPoint, bool>("touchedChangeSceneTrigger").Invoke(item.connectionToS3) = false;
}
}
Log.Info("Found A2_SG4_Logic a.k.a. Heng Power Reservoir flashback, calling A2_SG4_Logic.TrySkip() as a special case");
item.TrySkip();
Notifications.CancelNotification(activeA2SG4.Item2);
if (num)
{
activeA2SG4.Item1 = null;
}
}
else if ((Object)(object)activeA4S5.Item1 != (Object)null)
{
A4_S5_Logic item2 = activeA4S5.Item1;
if (item2.BossKilled.CurrentValue)
{
Log.Info("Found A4_S5_Logic a.k.a. Sky Rending Claw fight. Claw already killed. Applying special case logic to skip post-fight scene.");
item2.FinishCutscene.TrySkip();
}
else
{
if (((Component)item2.GianMechClawMonsterBase).gameObject.activeSelf)
{
Log.Info("Found A4_S5_Logic a.k.a. Sky Rending Claw fight. Claw not yet killed. But claw is already active, so trying to skip this now would just softlock. Doing nothing.");
return;
}
if (AccessTools.FieldRefAccess<BubbleDialogueController, int>("index").Invoke(item2.BeforeMangaBubble) >= item2.BeforeMangaBubble.nodes.Count && !AccessTools.FieldRefAccess<SimpleCutsceneManager, bool>("isMangaPauseing").Invoke(item2.StartCutscene))
{
Log.Info("Found A4_S5_Logic a.k.a. Sky Rending Claw fight. Appears to be in a manga transition animation, which in this scene would cause the fight to start without the screen activating so you can see it. Doing nothing for now; try again when the manga is done animating and waiting for input.");
return;
}
Log.Info("Found A4_S5_Logic a.k.a. Sky Rending Claw fight. Claw not yet killed. Applying special case logic to skip pre-fight scenes.");
GameObject.Find("A4_S5/A4_S5_Logic(DisableMeForBossDesign)/CUTSCENE_START/MangaView_OriginalPrefab/MANGACanvas").SetActive(false);
item2.BeforeMangaBubble.TrySkip();
item2.BubbleDialogue.TrySkip();
item2.TrySkip();
}
Notifications.CancelNotification(activeA4S5.Item2);
}
else if ((Object)(object)val != (Object)null && (Object)(object)AccessTools.FieldRefAccess<DialoguePlayer, DialogueGraph>("playingDialogueGraph").Invoke(val) != (Object)null)
{
Log.Info("calling DialoguePlayer.playingDialogueGraph.TrySkip()");
val.TrySkip();
if (dialogueSkipNotificationId != "")
{
Notifications.CancelNotification(dialogueSkipNotificationId);
dialogueSkipNotificationId = "";
}
}
else if ((Object)(object)activeCutscene.Item1 != (Object)null)
{
SimpleCutsceneManager item3 = activeCutscene.Item1;
Log.Info("calling TrySkip() on " + ((Object)item3).name);
AccessTools.Method(typeof(SimpleCutsceneManager), "TrySkip", (Type[])null, (Type[])null).Invoke(item3, Array.Empty<object>());
if (AccessTools.FieldRefAccess<SimpleCutsceneManager, bool>("isMangaPauseing").Invoke(item3))
{
Log.Info("also calling Resume() since it was 'manga paused'");
AccessTools.Method(typeof(SimpleCutsceneManager), "Resume", (Type[])null, (Type[])null).Invoke(item3, Array.Empty<object>());
}
Notifications.CancelNotification(activeCutscene.Item2);
activeCutscene = (null, "");
}
else if ((Object)(object)activeVideo.Item1 != (Object)null)
{
VideoPlayAction item4 = activeVideo.Item1;
Log.Info("calling TrySkip() on " + ((Object)item4).name);
AccessTools.Method(typeof(VideoPlayAction), "TrySkip", (Type[])null, (Type[])null).Invoke(item4, Array.Empty<object>());
Notifications.CancelNotification(activeVideo.Item2);
activeVideo = (null, "");
}
}
private void Update()
{
Notifications.Update();
}
private void OnDestroy()
{
harmony.UnpatchSelf();
Notifications.OnDestroy();
}
}
internal static class Log
{
private static ManualLogSource? logSource;
internal static void Init(ManualLogSource logSource)
{
Log.logSource = logSource;
}
internal static void Debug(object data)
{
ManualLogSource? obj = logSource;
if (obj != null)
{
obj.LogDebug(data);
}
}
internal static void Error(object data)
{
ManualLogSource? obj = logSource;
if (obj != null)
{
obj.LogError(data);
}
}
internal static void Fatal(object data)
{
ManualLogSource? obj = logSource;
if (obj != null)
{
obj.LogFatal(data);
}
}
internal static void Info(object data)
{
ManualLogSource? obj = logSource;
if (obj != null)
{
obj.LogInfo(data);
}
}
internal static void Message(object data)
{
ManualLogSource? obj = logSource;
if (obj != null)
{
obj.LogMessage(data);
}
}
internal static void Warning(object data)
{
ManualLogSource? obj = logSource;
if (obj != null)
{
obj.LogWarning(data);
}
}
}
internal class Notifications
{
private struct Notification
{
public string id;
public DateTimeOffset timestamp;
public string displayText;
}
private static Canvas CanvasComponent = null;
private static TextMeshProUGUI TextComponent = null;
private static bool isDirty = false;
private static List<Notification> notificationStack = new List<Notification>();
private static readonly TimeSpan expiry = TimeSpan.FromSeconds(10.0);
public static void Awake()
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Expected O, but got Unknown
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
GameObject val = new GameObject("NineSolsAPI-FullscreenCanvas");
RCGLifeCycle.DontDestroyForever(val);
CanvasComponent = val.AddComponent<Canvas>();
CanvasComponent.renderMode = (RenderMode)0;
TextComponent = val.AddComponent<TextMeshProUGUI>();
((TMP_Text)TextComponent).alignment = (TextAlignmentOptions)1028;
((TMP_Text)TextComponent).fontSize = 20f;
((Graphic)TextComponent).color = Color.white;
}
public static void Update()
{
UpdateText();
}
public static void OnDestroy()
{
Object.Destroy((Object)(object)((Component)CanvasComponent).gameObject);
}
public static void UpdateText()
{
DateTimeOffset now = DateTimeOffset.UtcNow;
int num = notificationStack.RemoveAll((Notification notification) => now - notification.timestamp > expiry);
isDirty |= num > 0;
if (isDirty)
{
((TMP_Text)TextComponent).text = string.Join('\n', notificationStack.Select((Notification n) => n.displayText));
isDirty = false;
}
}
public static string AddNotification(string displayText)
{
Log.Info("Notifications.AddNotification(" + displayText + ")");
string text = Guid.NewGuid().ToString("N");
notificationStack.Add(new Notification
{
id = text,
timestamp = DateTimeOffset.UtcNow,
displayText = displayText
});
isDirty = true;
return text;
}
public static void CancelNotification(string id)
{
string id2 = id;
Log.Info("Notifications.CancelNotification(" + id2 + ")");
if (id2 != null && id2 != "")
{
int num = notificationStack.RemoveAll((Notification x) => x.id == id2);
isDirty |= num > 0;
}
}
}
[HarmonyPatch]
public class Patches
{
private static List<string> skipDenylist = new List<string>
{
"A1_S2_GameLevel/Room/Prefab/Gameplay2_Alina/Simple Binding Tool/SimpleCutSceneFSM_關門戰開頭演出/FSM Animator/LogicRoot/[CutScene]", "GameLevel/Room/Prefab/村民避難所_階段 FSM Object/FSM Animator/View/村民避難所ControlRoom/Phase3(二次入侵)/General FSM A0_S10 二次入侵/FSM Animator/LogicRoot/[CutScene] 戰鬥前", "A2_S1/Room/Prefab/EnterPyramid_Acting/[CutScene]ActivePyramidAndEnter", "A3_S1/Room/Prefab/妹妹回憶_SimpleCutSceneFSM Variant/FSM Animator/LogicRoot/[CutScene]", "A4_S4/ZGunAndDoor/Shield Giant Bot Control Provider Variant_Cutscene/Hack Control Monster FSM/FSM Animator/LogicRoot/Cutscene/LogicRoot/[CutScene]", "A5_S5/Room/SimpleCutSceneFSM_JieChuan and Jee/FSM Animator/LogicRoot/[CutScene]", "AG_S2/Room/NPCs/議會演出相關Binding/ShanShan 軒軒分身 FSM/FSM Animator/CutScene/[CutScene] 食譜_團圓飯/FSM Animator/LogicRoot/[CutScene]", "GameLevel/Room/Prefab/EventBinder/General Boss Fight FSM Object Variant/FSM Animator/[CutScene] 易公死亡", "A3_S5_BossGouMang_GameLevel/Room/Simple Binding Tool/BossGouMangLogic/[CutScene]/[CutScene]Goumang_Explosion_Drop/[Timeline]Goumang_Explosion_Drop", "A5_S2/Room/SimpleCutSceneFSM_A5妹妹回憶/FSM Animator/LogicRoot/[CutScene]",
"GameLevel/Room1/SimpleCutSceneFSM/FSM Animator/LogicRoot/[CutScene]", "AG_S2/Room/Prefab/ControlRoom FSM Binding Tool/NPC_AICore_Base/NPC_AICore_Base_FSM/FSM Animator/LogicRoot/[CutScene]AI核心解鎖", "AG_S2/Room/NPCs/議會演出相關Binding/ShanShan 軒軒分身 FSM/FSM Animator/CutScene/收到文物演出/[CutsceneFSM] 軒軒收到古唱片/FSM Animator/LogicRoot/[CutScene]", "GameLevel/Room1/SimpleCutSceneFSM_EnterVilliage/FSM Animator/LogicRoot/[CutScene]", "A4_S3/Room/Prefab/CutScene_ChangeScene_FSM Variant/FSM Animator/LogicRoot/[CutScene]EnterScene", "A11_S2/CutScene_ChangeScene_FSM Variant/FSM Animator/LogicRoot/[CutScene]EnterScene", "AG_GoHome/Room/Prefab/SimpleCutSceneFSM_搭公車/FSM Animator/LogicRoot/[CutScene]", "A1_S1_GameLevel/Room/A1_S1_Tutorial_Logic/[CutScene]AfterTutorial_AI_Call/[Timeline]", "A4_S3/Room/Prefab/ElementRoom/ElementDoor FSM/ElementDoor FSM/FSM Animator/LogicRoot/[CutScene]Eenter_A4SG4", "A7_S1/Room/Prefab/A7_S1_三階段FSM/FSM Animator/Phase2_A7Entry/花入口 FSM Object/FSM Animator/LogicRoot/[CutScene] 進入演出",
"A2_S5_ BossHorseman_GameLevel/Room/Simple Binding Tool/Boss_SpearHorse_Logic/[CutScene]SpearHorse_End", "A0_S6/Room/Prefab/SimpleCutSceneFSM_道長死亡/FSM Animator/LogicRoot/Cutscene_TaoChangPart2", "A4_S5/A4_S5_Logic(DisableMeForBossDesign)/CUTSCENE_START", "A4_S5/A4_S5_Logic(DisableMeForBossDesign)/CUTSENE_EMERGENCY", "A4_S5/A4_S5_Logic(DisableMeForBossDesign)/CUTSCENE_Finish", "A11_S2/Room/Prefab/EventBinder/OldBoy FSM Object/FSM Animator/LogicRoot/[CutScene]OldBoyFighting/[Timeline]", "A2_Stage_Remake/Room/Prefab/FallingTeleportTrickBackgroundProvider/A7_HotSpring/溫泉場景Setting FSM Object/FSM Animator/View/SPA/PinkSkin/Pink/SimpleCutSceneFSM_八仙無限murmur/FSM Animator/LogicRoot/[CutScene]", "A2_Stage_Remake/Room/Prefab/FallingTeleportTrickBackgroundProvider/A7_HotSpring/溫泉場景Setting FSM Object/FSM Animator/View/SPA/PinkSkin/Pink_Odd/SimpleCutSceneFSM_八仙無限murmur/FSM Animator/LogicRoot/[CutScene]", "A7_ButterflyTest/Room/Prefab/FallingTeleportTrickBackgroundProvider/A7_HotSpring/溫泉場景Setting FSM Object/FSM Animator/View/SPA/PinkSkin/Pink_Odd/SimpleCutSceneFSM_八仙無限murmur/FSM Animator/LogicRoot/[CutScene]"
};
private static List<string> skipDelaylist = new List<string> { "A2_SG4/Room/妹妹回憶_SimpleCutSceneFSM/FSM Animator/LogicRoot/[CutScene]", "VR_TaoChang/Room/SimpleCutSceneFSM_易公後妹妹回憶/FSM Animator/LogicRoot/[CutScene]", "GameLevel/Room/Prefab/EventBinder/General Boss Fight FSM Object Variant/FSM Animator/[CutScene] 一進", "GameLevel/Room/Prefab/EventBinder/General Boss Fight FSM Object Variant/FSM Animator/[CutScene] 二進", "P2_R22_Savepoint_GameLevel/EventBinder/General Boss Fight FSM Object Variant/FSM Animator/[CutScene]FirstTimeContact/[Timeline]", "P2_R22_Savepoint_GameLevel/EventBinder/General Boss Fight FSM Object Variant/FSM Animator/[CutScene]SecondTimeContact/[Timeline]" };
private static HashSet<string> skippableVideos = new HashSet<string> { "GameLevel/Room/Prefab/SimpleCutSceneFSM_結局_大爆炸/--[States]/FSM/[State] PlayCutSceneEnd/[Action] VideoPlayAction", "A7_S6_Memory_Butterfly_CutScene_GameLevel/A7_S6_Cutscene FSM/--[States]/FSM/[State] PlayingVideo/[Action] VideoPlayAction" };
public static string GetFullPath(GameObject go)
{
Transform val = go.transform;
List<string> list = new List<string>();
while ((Object)(object)val != (Object)null)
{
list.Add(((Object)val).name);
val = val.parent;
}
list.Reverse();
return string.Join("/", list);
}
[HarmonyPrefix]
[HarmonyPatch(typeof(SimpleCutsceneManager), "PlayAnimation")]
private static void SimpleCutsceneManager_PlayAnimation(SimpleCutsceneManager __instance)
{
string fullPath = GetFullPath(((Component)__instance).gameObject);
if (skipDelaylist.Contains(fullPath))
{
return;
}
Log.Info("SimpleCutsceneManager_PlayAnimation " + fullPath);
if (skipDenylist.Contains(fullPath))
{
Log.Info("not allowing skip for cutscene " + fullPath + " because it's on the skip denylist");
return;
}
if (((Object)__instance).name.EndsWith("[TimeLine]CrateEnter_L") || ((Object)__instance).name.EndsWith("[TimeLine]CrateEnter_R"))
{
Log.Info("not allowing skip for " + fullPath + " because all crate exit 'cutscenes' I've tested instantly softlock when skipped");
return;
}
if (((Object)__instance).name == "[CutScene]調閱報告")
{
Log.Info("not allowing skip for " + fullPath + " because all \"[CutScene]調閱報告\" / Eigong lab report cutscenes risk softlocking when skipped");
return;
}
string item = "";
if ((Object)(object)((Component)__instance).gameObject == (Object)(object)GameObject.Find(CutsceneSkip.KuafuEndingChoiceCutsceneGOPath))
{
Log.Info("Not prompting player to skip this cutscene because it's in the Kuafu ending choice conversation, where even dialogue skipping softlocks.");
}
else if (((Object)__instance).name.EndsWith("_EnterScene"))
{
Log.Info("skipping notification for " + ((Object)__instance).name + " because transition 'cutscenes' are typically over before the player can even see the toast");
}
else
{
item = Notifications.AddNotification("Press " + CutsceneSkip.SkipKeybindText() + " to Skip This Cutscene");
}
CutsceneSkip.activeCutscene = (__instance, item);
}
[HarmonyPostfix]
[HarmonyPatch(typeof(SimpleCutsceneManager), "PlayAnimation")]
private static async void SimpleCutsceneManager_PlayAnimation_Postfix(SimpleCutsceneManager __instance)
{
string goPath = GetFullPath(((Component)__instance).gameObject);
if (skipDelaylist.Contains(goPath))
{
await UniTask.DelayFrame(100, (PlayerLoopTiming)8, default(CancellationToken));
Log.Info("SimpleCutsceneManager_PlayAnimation acting on " + goPath + " with delay (i.e. Postfix patch + 100 frame wait) to avoid softlocking");
string item = Notifications.AddNotification("Press " + CutsceneSkip.SkipKeybindText() + " to Skip This Cutscene");
CutsceneSkip.activeCutscene = (__instance, item);
}
}
[HarmonyPrefix]
[HarmonyPatch(typeof(SimpleCutsceneManager), "End")]
private static void SimpleCutsceneManager_End(SimpleCutsceneManager __instance)
{
Log.Info("SimpleCutsceneManager_End " + ((Object)__instance).name);
if ((Object)(object)CutsceneSkip.activeCutscene.Item1 == (Object)(object)__instance)
{
CutsceneSkip.activeCutscene = (null, "");
}
}
[HarmonyPrefix]
[HarmonyPatch(typeof(DialoguePlayer), "StartDialogue")]
private static void DialoguePlayer_StartDialogue(DialoguePlayer __instance)
{
if (CutsceneSkip.KuafuEndingChoiceCutsceneActive())
{
Log.Info("Not prompting player to skip this dialogue because it's in the Kuafu ending choice conversation, where even dialogue skipping softlocks.");
return;
}
Log.Info("DialoguePlayer_StartDialogue " + ((Object)__instance).name);
CutsceneSkip.dialogueSkipNotificationId = Notifications.AddNotification("Press " + CutsceneSkip.SkipKeybindText() + " to Skip This Dialogue");
}
[HarmonyPrefix]
[HarmonyPatch(typeof(VideoPlayAction), "OnStateEnterImplement")]
private static void VideoPlayAction_OnStateEnterImplement(VideoPlayAction __instance)
{
string fullPath = GetFullPath(((Component)__instance).gameObject);
Log.Debug("VideoPlayAction_OnStateEnterImplement " + fullPath);
if (skippableVideos.Contains(fullPath))
{
string item = Notifications.AddNotification("Press " + CutsceneSkip.SkipKeybindText() + " to Skip This Video");
CutsceneSkip.activeVideo = (__instance, item);
}
}
[HarmonyPrefix]
[HarmonyPatch(typeof(VideoPlayAction), "VideoClipDone")]
private static void VideoPlayAction_VideoClipDone(VideoPlayAction __instance)
{
Log.Debug("VideoPlayAction_VideoClipDone " + ((Object)__instance).name);
if ((Object)(object)CutsceneSkip.activeVideo.Item1 == (Object)(object)__instance)
{
CutsceneSkip.activeVideo = (null, "");
}
}
[HarmonyPrefix]
[HarmonyPatch(typeof(A2_SG4_Logic), "EnterLevelStart")]
private static void A2_SG4_Logic_EnterLevelStart(A2_SG4_Logic __instance)
{
Log.Info("A2_SG4_Logic_EnterLevelStart / Heng Power Reservoir flashback");
string item = Notifications.AddNotification("Press " + CutsceneSkip.SkipKeybindText() + " to Skip This Heng Flashback");
CutsceneSkip.activeA2SG4 = (__instance, item);
}
[HarmonyPrefix]
[HarmonyPatch(typeof(A2_SG4_Logic), "OnLevelDestroy")]
private static void A2_SG4_Logic_OnLevelDestroy(A2_SG4_Logic __instance)
{
Log.Info("A2_SG4_Logic_OnLevelDestroy / Heng Power Reservoir flashback");
if ((Object)(object)CutsceneSkip.activeA2SG4.Item1 == (Object)(object)__instance)
{
Notifications.CancelNotification(CutsceneSkip.activeA2SG4.Item2);
CutsceneSkip.activeA2SG4 = (null, "");
}
}
[HarmonyPrefix]
[HarmonyPatch(typeof(A4_S5_Logic), "EnterLevelStart")]
private static async void A4_S5_Logic_EnterLevelStart(A4_S5_Logic __instance)
{
Log.Info("A4_S5_Logic_EnterLevelStart / Sky Rending Claw Pre-Fight Scenes");
await UniTask.DelayFrame(100, (PlayerLoopTiming)8, default(CancellationToken));
string item = Notifications.AddNotification("Press " + CutsceneSkip.SkipKeybindText() + " to Skip Pre-Claw Fight Cutscenes");
CutsceneSkip.activeA4S5 = (__instance, item);
}
[HarmonyPrefix]
[HarmonyPatch(typeof(A4_S5_Logic), "FooGameComplete")]
private static void A4_S5_Logic_FooGameComplete(A4_S5_Logic __instance)
{
Log.Info("A4_S5_Logic_FooGameComplete / Sky Rending Claw Post-Fight Scenes");
if ((Object)(object)CutsceneSkip.activeA4S5.Item1 != (Object)null)
{
Notifications.CancelNotification(CutsceneSkip.activeA4S5.Item2);
}
string item = Notifications.AddNotification("Press " + CutsceneSkip.SkipKeybindText() + " to Skip Post-Claw Fight Cutscene");
CutsceneSkip.activeA4S5 = (__instance, item);
}
}
public static class PluginInfo
{
public const string PLUGIN_GUID = "CutsceneSkip";
public const string PLUGIN_NAME = "CutsceneSkip";
public const string PLUGIN_VERSION = "1.0.0";
}
}