Decompiled source of BluePrinceQoL v2.4.0
Mods/BluePrinceQoL.dll
Decompiled 3 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BluePrinceQoL; using BluePrinceQoL.Features; using BluePrinceQoL.Util; using HarmonyLib; using Il2Cpp; using Il2CppBluePrince.Inputs; using Il2CppBluePrince.UI; using Il2CppCosimo.Music; using Il2CppDG.Tweening; using Il2CppHutongGames.PlayMaker; using Il2CppHutongGames.PlayMaker.Actions; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem; using Il2CppSystem.Collections; using Il2CppSystem.Collections.Generic; using Il2CppSystem.Reflection; using Il2CppTMPro; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Audio; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; using UnityStandardAssets.Characters.FirstPerson; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(Core), "Blue Prince QoL", "2.4.0", "Lucas Pospor", null)] [assembly: MelonGame("Dogubomb", "BLUE PRINCE")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("Lucas Pospor")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Blue Prince QoL: quality-of-life tweaks for Blue Prince (MelonLoader)")] [assembly: AssemblyFileVersion("2.4.0.0")] [assembly: AssemblyInformationalVersion("2.4.0+feada09773b96526ab33c2c60d0d8eb449a18246")] [assembly: AssemblyProduct("BluePrinceQoL")] [assembly: AssemblyTitle("BluePrinceQoL")] [assembly: AssemblyVersion("2.4.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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 BluePrinceQoL { public class Core : MelonMod { internal static Instance Log; internal static MelonPreferences_Category Prefs; private readonly List<Feature> _features = new List<Feature>(); public override void OnInitializeMelon() { Log = ((MelonBase)this).LoggerInstance; Prefs = MelonPreferences.CreateCategory("BluePrinceQoL"); _features.Add(new StateLoggerFeature()); _features.Add(new HudFeature()); _features.Add(new IntroSkipFeature()); _features.Add(new PickupSkipFeature()); _features.Add(new TerminalFeature()); _features.Add(new CameraZoomFeature()); _features.Add(new MoveSpeedFeature()); _features.Add(new EndOfDayFeature()); _features.Add(new RunStatsFeature()); _features.Add(new DigSpeedFeature()); _features.Add(new StairsWarpFeature()); _features.Add(new BootSkipFeature()); _features.Add(new ClickUnlockFeature()); _features.Add(new DoorKeySpeedFeature()); _features.Add(new MusicVolumeFeature()); _features.Add(new CursorGuardFeature()); _features.Add(new DareGameOverFeature()); _features.Add(new SecretGardenFeature()); _features.Add(new ElevatorSpeedFeature()); _features.Add(new RotundaFeature()); _features.Add(new ModsMenuFeature()); _features.Add(new QuitWithoutSavingFeature()); _features.Add(new WorkshopFeature()); _features.Add(new SecurityVideoFeature()); _features.Add(new OfficeCompartmentFeature()); _features.Add(new DartsEquationFeature()); foreach (Feature feature in _features) { feature.Init(); } Log.Msg($"Loaded {_features.Count} feature(s)"); } public override void OnSceneWasInitialized(int buildIndex, string sceneName) { FsmHooks.OnSceneLoaded(); foreach (Feature f in _features) { Safe(f, delegate { f.OnSceneLoaded(sceneName); }); } } public override void OnUpdate() { FsmHooks.Tick(((MelonBase)this).HarmonyInstance); foreach (Feature feature in _features) { if (feature.Enabled) { Safe(feature, feature.OnUpdate); } } } public override void OnLateUpdate() { foreach (Feature feature in _features) { if (feature.Enabled) { Safe(feature, feature.OnLateUpdate); } } } public override void OnGUI() { foreach (Feature feature in _features) { if (feature.Enabled) { Safe(feature, feature.OnGUI); } } } private static void Safe(Feature f, Action a) { try { a(); } catch (Exception value) { f.Errors++; if (f.Errors <= 3) { Log.Error($"[{f.Name}] {value}"); } if (f.Errors == 3) { Log.Error("[" + f.Name + "] further errors suppressed"); } } } } } namespace BluePrinceQoL.Util { internal static class FsmHooks { private const float InstallDelay = 5f; private static float _installAt = -1f; public static bool Installed { get; private set; } public static event Action<FsmState> BeforeEnter; public static event Action<FsmState> AfterEnter; public static event Action<Harmony> InstallPatches; internal static void RaiseBefore(FsmState s) { Raise(FsmHooks.BeforeEnter, s); } internal static void RaiseAfter(FsmState s) { Raise(FsmHooks.AfterEnter, s); } private static void Raise(Action<FsmState> ev, FsmState s) { if (ev == null || s == null) { return; } Delegate[] invocationList = ev.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { Action<FsmState> action = (Action<FsmState>)invocationList[i]; try { action(s); } catch (Exception value) { Core.Log.Error($"FSM hook {action.Method.DeclaringType?.Name}: {value}"); } } } internal static void OnSceneLoaded() { if (!Installed && _installAt < 0f) { _installAt = Time.realtimeSinceStartup + 5f; } } internal static void Tick(Harmony harmony) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_0071: Expected O, but got Unknown if (Installed || _installAt < 0f || Time.realtimeSinceStartup < _installAt) { return; } Installed = true; MethodInfo methodInfo = AccessTools.Method(typeof(Fsm), "EnterState", (Type[])null, (Type[])null); harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(FsmHooks), "EnterStatePrefix", (Type[])null), new HarmonyMethod(typeof(FsmHooks), "EnterStatePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Core.Log.Msg("FSM hooks installed"); if (FsmHooks.InstallPatches == null) { return; } Delegate[] invocationList = FsmHooks.InstallPatches.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { Action<Harmony> action = (Action<Harmony>)invocationList[i]; try { action(harmony); } catch (Exception value) { Core.Log.Error($"Patch install {action.Method.DeclaringType?.Name}: {value}"); } } } private static void EnterStatePrefix(FsmState state) { RaiseBefore(state); } private static void EnterStatePostfix(FsmState state) { RaiseAfter(state); } public static string PathOf(GameObject go) { if ((Object)(object)go == (Object)null) { return ""; } string text = ((Object)go).name; Transform parent = go.transform.parent; while ((Object)(object)parent != (Object)null) { text = ((Object)parent).name + "/" + text; parent = parent.parent; } return text; } } internal static class FsmUtil { public static PlayMakerFSM Get(GameObject go, string fsmName = "FSM") { if ((Object)(object)go == (Object)null) { return null; } foreach (PlayMakerFSM component in go.GetComponents<PlayMakerFSM>()) { if (component.FsmName == fsmName) { return component; } } return null; } public static PlayMakerFSM Find(string goName, string fsmName = "FSM") { return Get(GameObject.Find(goName), fsmName); } public static FsmInt Int(PlayMakerFSM fsm, string name) { if (fsm == null) { return null; } return fsm.FsmVariables.GetFsmInt(name); } public static FsmFloat Float(PlayMakerFSM fsm, string name) { if (fsm == null) { return null; } return fsm.FsmVariables.GetFsmFloat(name); } public static FsmBool Bool(PlayMakerFSM fsm, string name) { if (fsm == null) { return null; } return fsm.FsmVariables.GetFsmBool(name); } public static FsmString String(PlayMakerFSM fsm, string name) { if (fsm == null) { return null; } return fsm.FsmVariables.GetFsmString(name); } } internal sealed class StateDurations { private sealed class Pending { public FsmState State; public readonly List<(FsmFloat f, float v)> Floats = new List<(FsmFloat, float)>(); public readonly List<string> Log = new List<string>(); } private readonly Dictionary<IntPtr, Pending> _pending = new Dictionary<IntPtr, Pending>(); private readonly List<IntPtr> _tmp = new List<IntPtr>(); private Pending _cur; public int Count => _pending.Count; public void OnEnter(FsmState state) { if (_pending.Count > 0 && _pending.TryGetValue(((Il2CppObjectBase)state).Pointer, out var value)) { Restore(((Il2CppObjectBase)state).Pointer, value); } } public void Begin(FsmState state) { _cur = new Pending { State = state }; } public void Scale(FsmFloat v, float factor, float min, string what) { if (_cur == null || !IsConst(v)) { return; } float value = v.Value; if (value <= 0f) { return; } float num = Math.Max(value * factor, min); if (!(num >= value)) { _cur.Floats.Add((v, value)); v.Value = num; if (what != null) { _cur.Log.Add($"{what} {value:0.##}s -> {num:0.##}s"); } } } public void Set(FsmFloat v, float target, string what) { if (_cur == null || !IsConst(v)) { return; } float value = v.Value; if (!(Math.Abs(value - target) < 1E-06f)) { _cur.Floats.Add((v, value)); v.Value = target; if (what != null) { _cur.Log.Add($"{what} {value:0.##} -> {target:0.##}"); } } } public string Commit() { Pending cur = _cur; _cur = null; if (cur == null || cur.Floats.Count == 0) { return null; } _pending[((Il2CppObjectBase)cur.State).Pointer] = cur; return string.Join(", ", cur.Log); } public void Tick() { if (_pending.Count == 0) { return; } _tmp.Clear(); foreach (KeyValuePair<IntPtr, Pending> item in _pending) { bool flag; try { flag = item.Value.State.Active; } catch { flag = false; } if (!flag) { _tmp.Add(item.Key); } } foreach (IntPtr item2 in _tmp) { Restore(item2, _pending[item2]); } } public void RestoreAll() { if (_pending.Count == 0) { return; } _tmp.Clear(); _tmp.AddRange(_pending.Keys); foreach (IntPtr item in _tmp) { Restore(item, _pending[item]); } } private void Restore(IntPtr ptr, Pending p) { _pending.Remove(ptr); foreach (var (val, value) in p.Floats) { try { val.Value = value; } catch { } } } private static bool IsConst(FsmFloat f) { if (f != null) { if (((NamedVariable)f).UseVariable) { return string.IsNullOrEmpty(((NamedVariable)f).Name); } return true; } return false; } } } namespace BluePrinceQoL.Features { public class BootSkipFeature : Feature { private const string SceneName = "Main Menu"; private const string MenuSystem = "Menu System"; private static readonly HashSet<string> SlateStates = new HashSet<string> { "State 3", "Logo Slates", "State 6", "State 7", "State 8" }; private const string ActivateState = "Activate Main Menu"; private const float SlateTime = 0.05f; private MelonPreferences_Entry<float> _menuFade; private MelonPreferences_Entry<bool> _hideLogos; private static readonly HashSet<string> LogoObjects = new HashSet<string> { "Dogubomb", "Raw Fury" }; public override string Name => "BootSkip"; protected override string Description => "Shorten the logo slates at game launch"; protected override void CreatePrefs() { _menuFade = Core.Prefs.CreateEntry<float>(Name + "_MenuFadeIn", 0.5f, "BootSkip: main menu fade-in (s)", "Fade-in of the main menu after the logos (vanilla: 1 s delay + 3 s fade).", false, false, (ValueValidator)null, (string)null); _hideLogos = Core.Prefs.CreateEntry<bool>(Name + "_HideLogos", true, "BootSkip: hide the logos", "Don't show the Dogubomb and Raw Fury logos at all (otherwise they flash by for a fraction of a second)", false, false, (ValueValidator)null, (string)null); } public override void OnSceneLoaded(string sceneName) { if (!base.Enabled || sceneName != "Main Menu") { return; } PlayMakerFSM val = FsmUtil.Find("Menu System"); if ((Object)(object)val == (Object)null) { Core.Log.Warning($"[{Name}] '{"Menu System"}' FSM not found; logos unchanged"); return; } int num = 0; int num2 = 0; float to = Mathf.Clamp(_menuFade.Value, 0.05f, 4f); foreach (FsmState item in (Il2CppArrayBase<FsmState>)(object)val.Fsm.States) { if (item == null) { continue; } bool flag = SlateStates.Contains(item.Name); bool flag2 = item.Name == "Activate Main Menu"; if (!flag && !flag2) { continue; } foreach (FsmStateAction item2 in (Il2CppArrayBase<FsmStateAction>)(object)item.Actions) { if (item2 == null) { continue; } if (flag && _hideLogos.Value && item2.Enabled) { ActivateGameObject val2 = ((Il2CppObjectBase)item2).TryCast<ActivateGameObject>(); if (val2 != null && val2.activate != null && val2.activate.Value) { GameObject ownerDefaultTarget = val.Fsm.GetOwnerDefaultTarget(val2.gameObject); if ((Object)(object)ownerDefaultTarget != (Object)null && LogoObjects.Contains(((Object)ownerDefaultTarget).name)) { item2.Enabled = false; num2++; continue; } } } Wait val3 = ((Il2CppObjectBase)item2).TryCast<Wait>(); if (val3 != null && flag && Shorten(val3.time, 0.05f)) { num++; } DotweenEaseColor val4 = ((Il2CppObjectBase)item2).TryCast<DotweenEaseColor>(); if (val4 == null) { continue; } if (flag) { if (Shorten(val4.duration, 0.05f)) { num++; } if (Shorten(val4.setDelay, 0f)) { num++; } } else { if (Shorten(val4.duration, to)) { num++; } if (Shorten(val4.setDelay, 0f)) { num++; } } } } if (num == 0) { Core.Log.Warning($"[{Name}] no logo waits/fades found on '{"Menu System"}'; logos unchanged"); } else { Core.Log.Msg($"[{Name}] shortened {num} logo timings" + ((num2 > 0) ? $", logos hidden ({num2})" : "")); } } private static bool Shorten(FsmFloat f, float to) { if (f == null || (((NamedVariable)f).UseVariable && !string.IsNullOrEmpty(((NamedVariable)f).Name))) { return false; } if (f.Value <= to) { return false; } f.Value = to; return true; } } public class CameraZoomFeature : Feature { private enum Scope { None, Terminal, Dining, Utility, Candle, Safe, Other } private enum Kind { None, PlayerTween, PlayerOther, ComputerRoot, Sender, DiningFade, PanelDelay, PanelDoor, CandleTweens, SafeOpen } private sealed class Info { public Kind Kind; public Scope Scope; public IntPtr Follow; public Wait FollowWait; public IntPtr After; public Wait AfterWait; } private const string PlayerObject = "FPSController - Prince"; private const string PlayerTweenState = "State 3"; private const string ComputerRoot = "Computer System"; private const string OverlayName = "UI OVERLAY CAM"; private const string CamDistanceVar = "CamDistance"; private const string PanelRoot = "FUSE BOX"; private const string PanelCollider = "Fuse Box - First Collider"; private const string PanelDoor = "S FuseboxDoor"; private const float PanelResetTime = 15f; private const string CandleTrigger = "Trigger"; private const string CandleRoot = "L1 Candlestick Handle"; private const string CandlePullState = "The Pull down"; private const string SafeOpenState = "OPEN + SPAWN"; private const string LetterLock = "Letters Lock Secret Tunnel"; private const float SafeMaxConstWait = 2f; private static readonly HashSet<string> FreezeEvents = new HashSet<string> { "CamTarget", "CamWarp", "QuickUnfreeze", "UnFreeze", "Freeze", "QuickFreeze", "InvisFreeze", "Camfreeze" }; private const float Eps = 0.0001f; private const float Margin = 0.03f; private const float FovMin = 0.05f; private const float ArmTime = 1.5f; private MelonPreferences_Entry<float> _speed; private MelonPreferences_Entry<bool> _instant; private MelonPreferences_Entry<bool> _terminals; private MelonPreferences_Entry<bool> _dining; private MelonPreferences_Entry<bool> _eatFade; private MelonPreferences_Entry<bool> _utility; private MelonPreferences_Entry<bool> _candle; private MelonPreferences_Entry<bool> _safes; private MelonPreferences_Entry<bool> _other; private readonly Dictionary<IntPtr, Info> _states = new Dictionary<IntPtr, Info>(); private readonly Dictionary<IntPtr, Scope> _fsmScope = new Dictionary<IntPtr, Scope>(); private readonly Dictionary<IntPtr, List<(FsmFloat f, float orig)>> _restoreAfter = new Dictionary<IntPtr, List<(FsmFloat, float)>>(); private readonly Dictionary<IntPtr, List<(FsmFloat f, float orig)>> _restoreOnEntry = new Dictionary<IntPtr, List<(FsmFloat, float)>>(); private IntPtr _armFsm; private IntPtr _armFollow; private Wait _armWait; private Scope _armScope; private float _armUntil = -1f; private bool _tweenScaled; private float _tweenDur; private FsmState _pendingFollow; private IntPtr _armAfter; private Wait _armAfterWait; private IntPtr _resetState; private Wait _resetWait; private Scope _resetScope; private float _resetUntil = -1f; private bool _tweenActive; private int _syncFrames; private bool _warnedPlayer; public override string Name => "CameraZoom"; protected override string Description => "Faster camera zoom onto terminals, the Dining Room plate, the Utility Closet fuse box and the Drawing Room safe (optionally safes, darts, maps ...)"; protected override void CreatePrefs() { _speed = Core.Prefs.CreateEntry<float>("CameraZoom_Speed", 4f, "CameraZoom: speed", "How many times faster the zoom runs (1 = vanilla, 0 = instant)", false, false, (ValueValidator)null, (string)null); _instant = Core.Prefs.CreateEntry<bool>("CameraZoom_Instant", false, "CameraZoom: instant", "Skip the zoom entirely (camera jumps to the vanilla end position); overrides Speed", false, false, (ValueValidator)null, (string)null); _terminals = Core.Prefs.CreateEntry<bool>("CameraZoom_Terminals", true, "CameraZoom: terminals", "Computer terminals (Security, Office, Laboratory, Bomb Shelter, Grotto): zoom in/out and FOV change", false, false, (ValueValidator)null, (string)null); _dining = Core.Prefs.CreateEntry<bool>("CameraZoom_DiningRoom", true, "CameraZoom: Dining Room", "Dining Room dinner plate: zoom in/out", false, false, (ValueValidator)null, (string)null); _eatFade = Core.Prefs.CreateEntry<bool>("CameraZoom_EatFade", true, "CameraZoom: eating fade", "Also speed up the 2 s fade to black + 2 s fade back when eating in the Dining Room", false, false, (ValueValidator)null, (string)null); _utility = Core.Prefs.CreateEntry<bool>("CameraZoom_UtilityPanel", true, "CameraZoom: Utility Closet panel", "Utility Closet fuse box / breaker panel: zoom in/out, its door, and the waits that cover the zoom-out", false, false, (ValueValidator)null, (string)null); _candle = Core.Prefs.CreateEntry<bool>("CameraZoom_DrawingRoomSafe", true, "CameraZoom: Drawing Room safe", "Drawing Room candlestick: the handle pull, the zoom onto the safe and the panel sliding up to reveal it", false, false, (ValueValidator)null, (string)null); _safes = Core.Prefs.CreateEntry<bool>("CameraZoom_SafesAndLocks", true, "CameraZoom: safes and locks", "Wall safes (Study, Boudoir, Office, Drawing Room) and the Watch Tower letter lock: zoom in and out, and the handle/door animation after the right code, before the camera pulls back", false, false, (ValueValidator)null, (string)null); _other = Core.Prefs.CreateEntry<bool>("CameraZoom_OtherObjects", false, "CameraZoom: other objects", "Also speed up identical zooms elsewhere (wall safes, dartboards, Room 46 map, Lab machine, Shrine/Curse menus ...)", false, false, (ValueValidator)null, (string)null); FsmHooks.BeforeEnter += OnBeforeEnter; FsmHooks.AfterEnter += OnAfterEnter; } public override void OnSceneLoaded(string sceneName) { _states.Clear(); _fsmScope.Clear(); _restoreAfter.Clear(); _restoreOnEntry.Clear(); Disarm(); ClearReset(); _tweenActive = false; _syncFrames = 0; } private float Factor() { if (_instant.Value) { return 0f; } float value = _speed.Value; if (value <= 0f) { return 0f; } if (!(value <= 1f)) { return 1f / value; } return 1f; } private bool Active(Scope scope) { if (!base.Enabled || Factor() >= 1f) { return false; } return scope switch { Scope.Terminal => _terminals.Value, Scope.Dining => _dining.Value, Scope.Utility => _utility.Value, Scope.Candle => _candle.Value, Scope.Safe => _safes.Value, Scope.Other => _other.Value, _ => false, }; } private void OnBeforeEnter(FsmState state) { IntPtr pointer = ((Il2CppObjectBase)state).Pointer; if (_restoreOnEntry.Count > 0 && _restoreOnEntry.Remove(pointer, out List<(FsmFloat, float)> value)) { foreach (var (val, value2) in value) { if (val != null) { val.Value = value2; } } } if (_resetState != IntPtr.Zero && pointer == _resetState) { ApplyPanelReset(state); } Info info = InfoOf(state); if (info.Kind == Kind.None) { return; } switch (info.Kind) { case Kind.PlayerTween: ScalePlayerTween(state); break; case Kind.PlayerOther: if (_tweenActive) { _tweenActive = false; _syncFrames = 3; } break; case Kind.ComputerRoot: if (Active(Scope.Terminal)) { ScaleFov(state); } break; case Kind.Sender: if (Active(info.Scope)) { Arm(state, info); ScaleFov(state); } break; case Kind.DiningFade: if (_eatFade.Value && Active(Scope.Dining)) { ScaleFades(state); } break; case Kind.PanelDelay: if (Active(Scope.Utility)) { ScaleDelays(state); } break; case Kind.PanelDoor: if (Active(Scope.Utility)) { ScaleDoor(state); } break; case Kind.CandleTweens: if (Active(Scope.Candle)) { ScaleCandleTweens(state); } break; case Kind.SafeOpen: if (Active(Scope.Safe)) { ScaleCandleTweens(state); } break; } } private void OnAfterEnter(FsmState state) { IntPtr pointer = ((Il2CppObjectBase)state).Pointer; if (_restoreAfter.Count > 0 && _restoreAfter.Remove(pointer, out List<(FsmFloat, float)> value)) { foreach (var (val, value2) in value) { if (val != null) { val.Value = value2; } } } if (!(_armFollow == IntPtr.Zero) && !(pointer != _armFollow) && state.Fsm != null && !(((Il2CppObjectBase)state.Fsm).Pointer != _armFsm)) { if (_tweenScaled) { ApplyFollow(state); } else { _pendingFollow = state; } } } public override void OnUpdate() { if (_syncFrames > 0) { _syncFrames--; Physics.SyncTransforms(); } if (_armUntil >= 0f && Time.unscaledTime > _armUntil && !_tweenScaled) { Disarm(); } if (_tweenScaled && _armFollow != IntPtr.Zero && Time.unscaledTime > _armUntil + 10f) { Disarm(); } if (_resetUntil >= 0f && Time.unscaledTime > _resetUntil) { ClearReset(); } } private void Arm(FsmState sender, Info info) { _armFsm = ((Il2CppObjectBase)sender.Fsm).Pointer; _armFollow = info.Follow; _armWait = info.FollowWait; _armScope = info.Scope; _armAfter = info.After; _armAfterWait = info.AfterWait; _armUntil = Time.unscaledTime + 1.5f; _tweenScaled = false; _tweenDur = 0f; _pendingFollow = null; } private void Disarm() { _armFsm = (_armFollow = IntPtr.Zero); _armWait = null; _armScope = Scope.None; _armAfter = IntPtr.Zero; _armAfterWait = null; _armUntil = -1f; _tweenScaled = false; _pendingFollow = null; } private void ScalePlayerTween(FsmState state) { //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Invalid comparison between Unknown and I4 if (_armFollow == IntPtr.Zero || _tweenScaled || Time.unscaledTime > _armUntil) { return; } if (!Active(_armScope)) { Disarm(); return; } FloatOperator val = null; FloatClamp val2 = null; DotweenMoveTransformTo val3 = null; foreach (FsmStateAction item in (Il2CppArrayBase<FsmStateAction>)(object)state.Actions) { if (item != null && item.Enabled) { if (val == null) { val = ((Il2CppObjectBase)item).TryCast<FloatOperator>(); } if (val2 == null) { val2 = ((Il2CppObjectBase)item).TryCast<FloatClamp>(); } if (val3 == null) { val3 = ((Il2CppObjectBase)item).TryCast<DotweenMoveTransformTo>(); } } } if (val == null || val2 == null || val3 == null || (int)val.operation != 2 || !IsVar(val2.floatVariable, "CamDistance") || !IsVar(val3.duration, "CamDistance") || !IsConst(val2.minValue) || !IsConst(val2.maxValue) || val.float1 == null || val.float2 == null) { if (!_warnedPlayer) { Core.Log.Warning($"[{Name}] player '{"State 3"}' doesn't look as expected; leaving zooms vanilla"); } _warnedPlayer = true; Disarm(); return; } float value = val2.minValue.Value; float value2 = val2.maxValue.Value; float num = Mathf.Clamp(val.float1.Value * val.float2.Value, value, value2); float num2 = Math.Max(num * Factor(), 0.0001f); AddRestore(_restoreAfter, ((Il2CppObjectBase)state).Pointer, val2.minValue, value); AddRestore(_restoreAfter, ((Il2CppObjectBase)state).Pointer, val2.maxValue, value2); val2.minValue.Value = num2; val2.maxValue.Value = num2; _tweenScaled = true; _tweenDur = num2; _tweenActive = true; Core.Log.Msg($"[{Name}] {_armScope} zoom {num:0.00}s -> {num2:0.00}s"); FsmState pendingFollow = _pendingFollow; _pendingFollow = null; if (pendingFollow != null) { Fsm fsm = pendingFollow.Fsm; if (((fsm != null) ? fsm.ActiveState : null) != null && ((Il2CppObjectBase)pendingFollow.Fsm.ActiveState).Pointer == ((Il2CppObjectBase)pendingFollow).Pointer) { ApplyFollow(pendingFollow); } } } private void ApplyFollow(FsmState state) { Wait armWait = _armWait; float tweenDur = _tweenDur; IntPtr armAfter = _armAfter; Wait armAfterWait = _armAfterWait; Scope armScope = _armScope; float num = Factor(); Disarm(); FsmFloat val = ((armWait != null) ? armWait.time : null); if (val == null) { return; } float num2 = Math.Max(val.Value * num, tweenDur) + 0.03f; if (!(num2 >= val.Value)) { if (!IsVar(val, "CamDistance")) { AddRestore(_restoreOnEntry, ((Il2CppObjectBase)state).Pointer, val, val.Value); } val.Value = num2; if (armAfter != IntPtr.Zero && armAfterWait != null) { _resetState = armAfter; _resetWait = armAfterWait; _resetScope = armScope; _resetUntil = Time.unscaledTime + 15f; } } } private void ClearReset() { _resetState = IntPtr.Zero; _resetWait = null; _resetScope = Scope.None; _resetUntil = -1f; } private void ApplyPanelReset(FsmState state) { Wait resetWait = _resetWait; FsmFloat val = ((resetWait != null) ? resetWait.time : null); Scope resetScope = _resetScope; ClearReset(); if (IsConst(val) && Active(resetScope)) { float value = val.Value; float num = Math.Max(value * Factor(), 0.0001f); if (!(num >= value)) { AddRestore(_restoreOnEntry, ((Il2CppObjectBase)state).Pointer, val, value); val.Value = num; } } } private void ScaleDelays(FsmState state) { float num = Factor(); foreach (FsmStateAction item in (Il2CppArrayBase<FsmStateAction>)(object)state.Actions) { SendEvent val = ((item != null) ? ((Il2CppObjectBase)item).TryCast<SendEvent>() : null); if (val != null && ((FsmStateAction)val).Enabled && IsConst(val.delay)) { float value = val.delay.Value; float num2 = value * num; if (!(value <= 0f) && !(num2 >= value)) { AddRestore(_restoreAfter, ((Il2CppObjectBase)state).Pointer, val.delay, value); val.delay.Value = num2; } } } } private void ScaleDoor(FsmState state) { float num = Factor(); foreach (FsmStateAction item in (Il2CppArrayBase<FsmStateAction>)(object)state.Actions) { if (item == null || !item.Enabled) { continue; } FloatClamp val = ((Il2CppObjectBase)item).TryCast<FloatClamp>(); if (val != null && !val.everyFrame && IsConst(val.minValue) && IsConst(val.maxValue)) { AddRestore(_restoreAfter, ((Il2CppObjectBase)state).Pointer, val.minValue, val.minValue.Value); AddRestore(_restoreAfter, ((Il2CppObjectBase)state).Pointer, val.maxValue, val.maxValue.Value); val.minValue.Value = Math.Max(val.minValue.Value * num, 0.0001f); val.maxValue.Value = Math.Max(val.maxValue.Value * num, 0.0001f); continue; } DotweenRotateTransformTo val2 = ((Il2CppObjectBase)item).TryCast<DotweenRotateTransformTo>(); if (val2 != null && IsConst(val2.duration)) { float value = val2.duration.Value; float num2 = Math.Max(value * num, 0.0001f); if (!(num2 >= value)) { AddRestore(_restoreOnEntry, ((Il2CppObjectBase)state).Pointer, val2.duration, value); val2.duration.Value = num2; } } } } private void ScaleCandleTweens(FsmState state) { float num = Factor(); foreach (FsmStateAction item in (Il2CppArrayBase<FsmStateAction>)(object)state.Actions) { if (item == null || !item.Enabled) { continue; } FsmFloat val = null; FsmFloat val2 = null; FsmBool val3 = null; DotweenRotateTransformTo val4 = ((Il2CppObjectBase)item).TryCast<DotweenRotateTransformTo>(); if (val4 != null) { val = val4.duration; val2 = val4.setDelay; val3 = val4.isSpeedBased; } else { DotweenMoveTransformTo val5 = ((Il2CppObjectBase)item).TryCast<DotweenMoveTransformTo>(); if (val5 == null) { continue; } val = val5.duration; val2 = val5.setDelay; val3 = val5.isSpeedBased; } if (val3 != null && !((NamedVariable)val3).IsNone && val3.Value) { continue; } FsmFloat[] array = (FsmFloat[])(object)new FsmFloat[2] { val, val2 }; foreach (FsmFloat val6 in array) { if (IsConst(val6) && !(val6.Value <= 0f)) { AddRestore(_restoreAfter, ((Il2CppObjectBase)state).Pointer, val6, val6.Value); val6.Value = Math.Max(val6.Value * num, (val6 == val2) ? 0f : 0.0001f); } } } } private void ScaleFov(FsmState state) { float num = Factor(); foreach (FsmStateAction item in (Il2CppArrayBase<FsmStateAction>)(object)state.Actions) { EnableFOVOffsetAction val = ((item != null) ? ((Il2CppObjectBase)item).TryCast<EnableFOVOffsetAction>() : null); if (val != null && ((FsmStateAction)val).Enabled && IsConst(val.FadeTime)) { float value = val.FadeTime.Value; float num2 = Math.Max(value * num, 0.05f); if (!(num2 >= value)) { AddRestore(_restoreAfter, ((Il2CppObjectBase)state).Pointer, val.FadeTime, value); val.FadeTime.Value = num2; } } } } private void ScaleFades(FsmState state) { float num = Factor(); foreach (FsmStateAction item in (Il2CppArrayBase<FsmStateAction>)(object)state.Actions) { if (item == null || !item.Enabled) { continue; } CameraFadeOut obj = ((Il2CppObjectBase)item).TryCast<CameraFadeOut>(); object obj2 = ((obj != null) ? obj.time : null); if (obj2 == null) { CameraFadeIn obj3 = ((Il2CppObjectBase)item).TryCast<CameraFadeIn>(); obj2 = ((obj3 != null) ? obj3.time : null); } FsmFloat val = (FsmFloat)obj2; if (IsConst(val)) { float value = val.Value; float num2 = Math.Max(value * num, 0.0001f); if (!(num2 >= value)) { AddRestore(_restoreOnEntry, ((Il2CppObjectBase)state).Pointer, val, value); val.Value = num2; } } } } private Info InfoOf(FsmState state) { if (_states.TryGetValue(((Il2CppObjectBase)state).Pointer, out var value)) { return value; } value = Classify(state) ?? new Info(); _states[((Il2CppObjectBase)state).Pointer] = value; return value; } private Info Classify(FsmState state) { Fsm fsm = state.Fsm; GameObject val = ((fsm != null) ? fsm.GameObject : null); if ((Object)(object)val == (Object)null || state.Actions == null) { return null; } string name = ((Object)val).name; if (name == "FPSController - Prince") { if (fsm.Name != "FSM") { return null; } return new Info { Kind = ((state.Name == "State 3") ? Kind.PlayerTween : Kind.PlayerOther) }; } if (name == "Computer System") { Transform parent = val.transform.parent; if ((Object)(object)parent == (Object)null || ((Object)parent).name != "UI OVERLAY CAM") { return null; } if (!HasAction<EnableFOVOffsetAction>(state)) { return null; } return new Info { Kind = Kind.ComputerRoot }; } if (SendsCamTarget(state)) { Scope scope = ScopeOf(fsm, val); (FsmState, Wait) tuple = FollowOf(state, scope); if (tuple.Item1 == null) { return null; } Info info = new Info { Kind = Kind.Sender, Scope = scope, Follow = ((Il2CppObjectBase)tuple.Item1).Pointer, FollowWait = tuple.Item2 }; if ((scope == Scope.Utility && IsConst(tuple.Item2.time)) || scope == Scope.Safe) { (FsmState, Wait) tuple2 = PanelResetOf(tuple.Item1, scope); if (tuple2.Item1 != null) { info.After = ((Il2CppObjectBase)tuple2.Item1).Pointer; info.AfterWait = tuple2.Item2; } } return info; } if ((HasAction<CameraFadeOut>(state) || HasAction<CameraFadeIn>(state)) && ScopeOf(fsm, val) == Scope.Dining) { return new Info { Kind = Kind.DiningFade, Scope = Scope.Dining }; } if (name == "Fuse Box - First Collider" && ScopeOf(fsm, val) == Scope.Utility && HasDelayedSend(state)) { return new Info { Kind = Kind.PanelDelay, Scope = Scope.Utility }; } if (name.StartsWith("Digits", StringComparison.Ordinal) && fsm.Name == "FSM" && state.Name == "OPEN + SPAWN" && ScopeOf(fsm, val) == Scope.Safe) { return new Info { Kind = Kind.SafeOpen, Scope = Scope.Safe }; } if (name == "Trigger" && IsUnder(val, "L1 Candlestick Handle") && fsm.Name == "FSM" && (state.Name == "The Pull down" || FollowIsCandleZoom(state))) { return new Info { Kind = Kind.CandleTweens, Scope = Scope.Candle }; } if (name == "S FuseboxDoor" && IsUnder(val, "FUSE BOX") && (HasAction<FloatClamp>(state) || HasAction<DotweenRotateTransformTo>(state))) { return new Info { Kind = Kind.PanelDoor, Scope = Scope.Utility }; } return null; } private static bool FollowIsCandleZoom(FsmState state) { Fsm fsm = state.Fsm; if (((fsm != null) ? fsm.States : null) == null) { return false; } foreach (FsmState item in (Il2CppArrayBase<FsmState>)(object)state.Fsm.States) { if (((item != null) ? item.Actions : null) != null && SendsCamTarget(item)) { FsmState obj = FinishedTarget(item); if (((obj != null) ? new IntPtr?(((Il2CppObjectBase)obj).Pointer) : ((IntPtr?)null)) == ((Il2CppObjectBase)state).Pointer) { return true; } } } return false; } private static bool HasDelayedSend(FsmState state) { foreach (FsmStateAction item in (Il2CppArrayBase<FsmStateAction>)(object)state.Actions) { SendEvent val = ((item != null) ? ((Il2CppObjectBase)item).TryCast<SendEvent>() : null); if (val != null && ((FsmStateAction)val).Enabled && IsConst(val.delay) && val.delay.Value > 0f) { return true; } } return false; } private static bool IsUnder(GameObject go, string parentName) { Transform parent = go.transform.parent; if ((Object)(object)parent != (Object)null) { return ((Object)parent).name == parentName; } return false; } private static (FsmState state, Wait wait) PanelResetOf(FsmState follow, Scope scope) { FsmState val = FinishedTarget(follow); if (((val != null) ? val.Actions : null) == null) { return default((FsmState, Wait)); } Wait val2 = null; foreach (FsmStateAction item in (Il2CppArrayBase<FsmStateAction>)(object)val.Actions) { if (item == null || !item.Enabled) { continue; } Wait val3 = ((Il2CppObjectBase)item).TryCast<Wait>(); if (val3 != null) { if (val2 != null || val3.realTime) { return default((FsmState, Wait)); } val2 = val3; } else { if (((Il2CppObjectBase)item).TryCast<SetFsmInt>() != null || ((Il2CppObjectBase)item).TryCast<SetProperty>() != null) { continue; } if (scope == Scope.Safe && val2 != null && val.IsSequence) { SendEvent val4 = ((Il2CppObjectBase)item).TryCast<SendEvent>(); if (val4 != null && !val4.everyFrame) { FsmEvent sendEvent = val4.sendEvent; if (((sendEvent != null) ? sendEvent.Name : null) == "QuickUnfreeze" && IsConst(val4.delay) && val4.delay.Value <= 0f) { continue; } } } return default((FsmState, Wait)); } } if (!IsConst((val2 != null) ? val2.time : null)) { return default((FsmState, Wait)); } return (state: val, wait: val2); } private static FsmState FinishedTarget(FsmState state) { if (state.Transitions == null) { return null; } foreach (FsmTransition item in (Il2CppArrayBase<FsmTransition>)(object)state.Transitions) { if (item != null && item.EventName == "FINISHED") { return string.IsNullOrEmpty(item.ToState) ? null : state.Fsm.GetState(item.ToState); } } return null; } private static bool SendsCamTarget(FsmState state) { foreach (FsmStateAction item in (Il2CppArrayBase<FsmStateAction>)(object)state.Actions) { SendEvent val = ((item != null) ? ((Il2CppObjectBase)item).TryCast<SendEvent>() : null); if (val != null && ((FsmStateAction)val).Enabled) { FsmEvent sendEvent = val.sendEvent; if (((sendEvent != null) ? sendEvent.Name : null) == "CamTarget") { return true; } } } return false; } private Scope ScopeOf(Fsm fsm, GameObject go) { if (_fsmScope.TryGetValue(((Il2CppObjectBase)fsm).Pointer, out var value)) { return value; } value = Scope.Other; string name = ((Object)go).name; if (name == "Click Trigger" && OpensTerminal(fsm)) { value = Scope.Terminal; } else if (name == "Fuse Box - First Collider" && IsUnder(go, "FUSE BOX")) { value = Scope.Utility; } else if (name == "Trigger" && IsUnder(go, "L1 Candlestick Handle")) { value = Scope.Candle; } else if (IsSafe(go)) { value = Scope.Safe; } else if ((name == "C - Dining Room Dishes" || name == "Course Trigger" || (name.StartsWith("C - ", StringComparison.Ordinal) && name.EndsWith("Dish Click", StringComparison.Ordinal))) && FsmHooks.PathOf(go).Contains("Dining Room")) { value = Scope.Dining; } _fsmScope[((Il2CppObjectBase)fsm).Pointer] = value; return value; } private static bool IsSafe(GameObject go) { string name = ((Object)go).name; if (name == "Letters Lock Secret Tunnel") { return true; } Transform parent = go.transform.parent; Transform val = (name.StartsWith("Digits", StringComparison.Ordinal) ? go.transform : ((name == "First Collider" && (Object)(object)parent != (Object)null && ((Object)parent).name.StartsWith("Digits", StringComparison.Ordinal)) ? parent : null)); if ((Object)(object)val == (Object)null) { return false; } return FsmHooks.PathOf(((Component)val).gameObject).Contains("Safe", StringComparison.OrdinalIgnoreCase); } private static bool OpensTerminal(Fsm fsm) { FsmState state = fsm.GetState("Wait for Exit 2"); if (((state != null) ? state.Actions : null) == null) { return false; } foreach (FsmStateAction item in (Il2CppArrayBase<FsmStateAction>)(object)state.Actions) { GetChild val = ((item != null) ? ((Il2CppObjectBase)item).TryCast<GetChild>() : null); if (((val != null) ? val.childName : null) != null && val.childName.Value == "Computer System") { return true; } } return false; } private static (FsmState state, Wait wait) FollowOf(FsmState sender, Scope scope) { FsmState val = FinishedTarget(sender); if (((val != null) ? val.Actions : null) == null) { return default((FsmState, Wait)); } Wait val2 = null; foreach (FsmStateAction item in (Il2CppArrayBase<FsmStateAction>)(object)val.Actions) { if (item == null || !item.Enabled) { continue; } Wait val3 = ((Il2CppObjectBase)item).TryCast<Wait>(); if (val3 != null) { if (val2 != null || val3.realTime) { return default((FsmState, Wait)); } val2 = val3; continue; } FloatOperator val4 = ((Il2CppObjectBase)item).TryCast<FloatOperator>(); if (val4 != null) { if (!val4.everyFrame) { continue; } return default((FsmState, Wait)); } FloatClamp val5 = ((Il2CppObjectBase)item).TryCast<FloatClamp>(); if (val5 != null) { if (val5.everyFrame) { return default((FsmState, Wait)); } } else { if (((Il2CppObjectBase)item).TryCast<SetFsmInt>() != null) { continue; } if (scope == Scope.Utility) { SendEvent val6 = ((Il2CppObjectBase)item).TryCast<SendEvent>(); if (val6 != null && !val6.everyFrame) { FsmEvent sendEvent = val6.sendEvent; if (((sendEvent != null) ? sendEvent.Name : null) != "CamTarget") { continue; } } } if (scope == Scope.Safe) { SendEvent val7 = ((Il2CppObjectBase)item).TryCast<SendEvent>(); if (val7 != null && !val7.everyFrame && IsConst(val7.delay) && val7.delay.Value <= 0f) { HashSet<string> freezeEvents = FreezeEvents; FsmEvent sendEvent2 = val7.sendEvent; if (!freezeEvents.Contains(((sendEvent2 != null) ? sendEvent2.Name : null) ?? "")) { continue; } } } if (scope != Scope.Candle || (((Il2CppObjectBase)item).TryCast<ActivateGameObject>() == null && ((Il2CppObjectBase)item).TryCast<DotweenMoveTransformTo>() == null)) { return default((FsmState, Wait)); } } } if (((val2 != null) ? val2.time : null) == null) { return default((FsmState, Wait)); } if (IsVar(val2.time, "CamDistance")) { return (state: val, wait: val2); } if ((scope == Scope.Dining || scope == Scope.Utility) && IsConst(val2.time)) { return (state: val, wait: val2); } if (scope == Scope.Safe && IsConst(val2.time) && val2.time.Value <= 2f) { return (state: val, wait: val2); } return default((FsmState, Wait)); } private static bool HasAction<T>(FsmState state) where T : FsmStateAction { foreach (FsmStateAction item in (Il2CppArrayBase<FsmStateAction>)(object)state.Actions) { if (item != null && item.Enabled && ((Il2CppObjectBase)item).TryCast<T>() != null) { return true; } } return false; } private static void AddRestore(Dictionary<IntPtr, List<(FsmFloat, float)>> d, IntPtr state, FsmFloat f, float orig) { if (!d.TryGetValue(state, out var value)) { value = (d[state] = new List<(FsmFloat, float)>()); } foreach (var item in value) { if (((Il2CppObjectBase)item.Item1).Pointer == ((Il2CppObjectBase)f).Pointer) { return; } } value.Add((f, orig)); } private static bool IsConst(FsmFloat f) { if (f != null) { if (((NamedVariable)f).UseVariable) { return string.IsNullOrEmpty(((NamedVariable)f).Name); } return true; } return false; } private static bool IsVar(FsmFloat f, string name) { if (f != null && ((NamedVariable)f).UseVariable) { return ((NamedVariable)f).Name == name; } return false; } } public class ClickUnlockFeature : Feature { private enum Kind { None, Player, Gm } private sealed class Info { public Kind Kind; public Wait Wait; } private sealed class Pending { public Kind Kind; public Fsm Fsm; public FsmState State; public Wait Wait; public int Frame; public float EntryTime; public bool Released; public int ReleasedFrame; public float Vanilla; public FsmFloat Shortened; public float ShortenedOrig; public bool Blocked; } private const string PlayerObject = "FPSController - Prince"; private const string PlayerPath = "__SYSTEM/FPS Home/FPSController - Prince"; private const string GmObject = "Global Manager"; private const string DraftUiPath = "__SYSTEM/THE DRAFT/anchor/DRAFT UI"; private const string DraftIdleState = "Listener"; private const string PlayerDoneState = "State 7"; private const string GmDoneState = "State 41"; private const string UnfreezeState = "UNFROZEN 2"; private const string FadeUnfreezeState = "UNFROZEN 3"; private const string QuickUnfreezeState = "UNFROZEN"; private static readonly HashSet<string> GmWaitStates = new HashSet<string> { "State 40", "State 42", "State 43" }; private static readonly HashSet<string> PlayerUnlockedStates = new HashSet<string> { "State 7", "State 14", "State 17" }; private const int MinFrames = 2; private const int FallbackFrames = 5; private const float FallbackTime = 0.0001f; private MelonPreferences_Entry<float> _delay; private MelonPreferences_Entry<bool> _afterDrafts; private MelonPreferences_Entry<bool> _quick; private MelonPreferences_Entry<bool> _crosshair; private MelonPreferences_Entry<bool> _log; private readonly Dictionary<IntPtr, Info> _states = new Dictionary<IntPtr, Info>(); private Pending _player; private Pending _gm; private Fsm _playerFsm; private Fsm _draftFsm; private float _nextDraftSearch; private bool _warnedDraft; private bool _warnedShape; private bool _warnedFallback; private int _releases; public override string Name => "ClickUnlock"; protected override string Description => "Shorter click lock-out after closing cards, menus and documents (camera/crosshair come back sooner)"; protected override void CreatePrefs() { _delay = Core.Prefs.CreateEntry<float>("ClickUnlock_Delay", 0.1f, "ClickUnlock: delay", "Seconds after an unfreeze before you can click the world again (vanilla: 0.99 after cards/menus, 0.6 after drafts). Never less than two frames, whatever the value.", false, false, (ValueValidator)null, (string)null); _afterDrafts = Core.Prefs.CreateEntry<bool>("ClickUnlock_AfterDrafts", true, "ClickUnlock: after drafts", "Also shorten the 0.4 s lock after walking through a freshly drafted door (still waits for the draft screen to reset)", false, false, (ValueValidator)null, (string)null); _quick = Core.Prefs.CreateEntry<bool>("ClickUnlock_AfterZooms", true, "ClickUnlock: after zooms", "Also shorten the 0.1 s after a quick unfreeze (leaving a safe, terminal or other zoomed-in object) to two frames, so the crosshair comes back as soon as the camera is back", false, false, (ValueValidator)null, (string)null); _crosshair = Core.Prefs.CreateEntry<bool>("ClickUnlock_Crosshair", true, "ClickUnlock: crosshair", "Also bring the crosshair back as soon as clicking works again (Global Manager cursor waits: 1.1 s cards, 0.8 s dig cards, 2 s drafts)", false, false, (ValueValidator)null, (string)null); _log = Core.Prefs.CreateEntry<bool>("ClickUnlock_Log", false, "ClickUnlock: log", "Log every shortened lock and why one is held back (for testing; the first 3 are always logged)", false, false, (ValueValidator)null, (string)null); FsmHooks.BeforeEnter += OnBeforeEnter; FsmHooks.AfterEnter += OnAfterEnter; } public override void OnSceneLoaded(string sceneName) { RestoreFallback(_player); RestoreFallback(_gm); _player = (_gm = null); _states.Clear(); _playerFsm = null; _draftFsm = null; _nextDraftSearch = 0f; } private float Delay() { float value = _delay.Value; if (!float.IsNaN(value) && !(value < 0f)) { return value; } return 0f; } private void OnBeforeEnter(FsmState state) { if (_player?.Shortened != null && ((Il2CppObjectBase)state).Pointer == ((Il2CppObjectBase)_player.State).Pointer) { RestoreFallback(_player); _player = null; } if (_gm?.Shortened != null && ((Il2CppObjectBase)state).Pointer == ((Il2CppObjectBase)_gm.State).Pointer) { RestoreFallback(_gm); _gm = null; } } private void OnAfterEnter(FsmState state) { string name = state.Name; switch (name) { default: if (!PlayerUnlockedStates.Contains(name) && !GmWaitStates.Contains(name)) { return; } break; case "UNFROZEN 2": case "UNFROZEN 3": case "UNFROZEN": break; } Info info = InfoOf(state); if (info.Kind == Kind.Player) { _playerFsm = state.Fsm; } if (info.Wait != null && base.Enabled && (info.Kind != Kind.Player || !(name == "UNFROZEN 3") || _afterDrafts.Value) && (info.Kind != Kind.Player || !(name == "UNFROZEN") || _quick.Value) && (info.Kind != Kind.Gm || _crosshair.Value)) { Pending obj = new Pending { Kind = info.Kind, Fsm = state.Fsm, State = state, Wait = info.Wait, Frame = Time.frameCount, EntryTime = Time.time }; FsmFloat time = info.Wait.time; obj.Vanilla = ((time != null) ? time.Value : 0f); Pending pending = obj; if (info.Kind == Kind.Player) { RestoreFallback(_player); _player = pending; } else { RestoreFallback(_gm); _gm = pending; } } } public override void OnUpdate() { _player = Step(_player); _gm = Step(_gm); } private Pending Step(Pending p) { if (p == null) { return null; } Fsm fsm = p.Fsm; FsmState val = ((fsm != null) ? fsm.ActiveState : null); if (val == null || ((Il2CppObjectBase)val).Pointer != ((Il2CppObjectBase)p.State).Pointer) { RestoreFallback(p); return null; } int frameCount = Time.frameCount; if (p.Released) { if (p.Shortened == null && frameCount > p.ReleasedFrame + 5) { ApplyFallback(p); } return p; } if (frameCount - p.Frame < 2) { return p; } if (p.State.Name != "UNFROZEN" && Time.time - p.EntryTime < Delay()) { return p; } Wait wait = p.Wait; FsmFloat time = wait.time; if (time == null || wait.timer >= time.Value) { return p; } string text = Gate(p); if (text != null) { if (_log.Value && !p.Blocked) { Core.Log.Msg($"[{Name}] holding '{p.State.Name}' ({text})"); } p.Blocked = true; return p; } float value = Time.time - p.EntryTime; wait.timer = time.Value; p.Released = true; p.ReleasedFrame = frameCount; if (_log.Value || _releases < 3) { Core.Log.Msg($"[{Name}] {Label(p)}: {p.Vanilla:0.00}s -> {value:0.00}s"); } _releases++; return p; } private string Gate(Pending p) { Fsm val = DraftFsm(); if (val == null) { return "DRAFT UI not found"; } string activeStateName = val.ActiveStateName; if (activeStateName != "Listener") { return "DRAFT UI in '" + activeStateName + "'"; } if (p.Kind == Kind.Gm) { Fsm obj = _playerFsm; if (obj == null) { PlayMakerFSM obj2 = FsmUtil.Find("__SYSTEM/FPS Home/FPSController - Prince"); obj = (_playerFsm = ((obj2 != null) ? obj2.Fsm : null)); } Fsm val2 = obj; if (val2 == null) { return "player FSM not found"; } string activeStateName2 = val2.ActiveStateName; if (activeStateName2 == null || !PlayerUnlockedStates.Contains(activeStateName2)) { return "player in '" + activeStateName2 + "'"; } FsmVariables variables = val2.Variables; FsmBool val3 = ((variables != null) ? variables.GetFsmBool("currently frozen") : null); if (val3 != null && val3.Value) { return "player frozen"; } } return null; } private void ApplyFallback(Pending p) { FsmFloat time = p.Wait.time; if (time != null && (!((NamedVariable)time).UseVariable || string.IsNullOrEmpty(((NamedVariable)time).Name))) { if (!_warnedFallback) { Core.Log.Warning($"[{Name}] '{p.State.Name}' didn't finish after its timer was fast-forwarded; shortening its time instead"); } _warnedFallback = true; p.Shortened = time; p.ShortenedOrig = time.Value; time.Value = 0.0001f; } } private static void RestoreFallback(Pending p) { if (p?.Shortened != null) { if (Math.Abs(p.Shortened.Value - 0.0001f) < 1E-07f) { p.Shortened.Value = p.ShortenedOrig; } p.Shortened = null; } } private Fsm DraftFsm() { if (_draftFsm != null) { return _draftFsm; } if (Time.unscaledTime < _nextDraftSearch) { return null; } _nextDraftSearch = Time.unscaledTime + 1f; PlayMakerFSM obj = FsmUtil.Find("__SYSTEM/THE DRAFT/anchor/DRAFT UI"); _draftFsm = ((obj != null) ? obj.Fsm : null); if (_draftFsm == null && !_warnedDraft) { _warnedDraft = true; Core.Log.Warning($"[{Name}] '{"__SYSTEM/THE DRAFT/anchor/DRAFT UI"}' not found; click locks stay vanilla until it is"); } return _draftFsm; } private static string Label(Pending p) { if (p.Kind != Kind.Player) { return "crosshair (" + p.State.Name + ")"; } if (!(p.State.Name == "UNFROZEN 3")) { if (!(p.State.Name == "UNFROZEN")) { return "unlock after unfreeze"; } return "unlock after zoom"; } return "unlock after draft"; } private Info InfoOf(FsmState state) { if (_states.TryGetValue(((Il2CppObjectBase)state).Pointer, out var value)) { return value; } value = Classify(state) ?? new Info(); _states[((Il2CppObjectBase)state).Pointer] = value; return value; } private Info Classify(FsmState state) { Fsm fsm = state.Fsm; GameObject val = ((fsm != null) ? fsm.GameObject : null); if ((Object)(object)val == (Object)null || fsm.Name != "FSM") { return null; } string name = state.Name; if (((Object)val).name == "FPSController - Prince") { if (FsmHooks.PathOf(val) != "__SYSTEM/FPS Home/FPSController - Prince") { return null; } if (name != "UNFROZEN 2" && name != "UNFROZEN 3" && name != "UNFROZEN") { return new Info { Kind = Kind.Player }; } Wait val2 = SoleWait(state, "State 7"); if (val2 == null) { WarnShape("player '" + name + "'"); } return new Info { Kind = Kind.Player, Wait = val2 }; } if (((Object)val).name == "Global Manager" && GmWaitStates.Contains(name)) { Wait val3 = SoleWait(state, "State 41"); if (val3 != null && !OnlySetFsmInt(fsm.GetState("State 41"))) { val3 = null; } if (val3 == null) { WarnShape("Global Manager '" + name + "'"); return null; } return new Info { Kind = Kind.Gm, Wait = val3 }; } return null; } private static Wait SoleWait(FsmState state, string next) { if (state.Actions == null || state.Transitions == null) { return null; } string text = null; foreach (FsmTransition item in (Il2CppArrayBase<FsmTransition>)(object)state.Transitions) { if (item != null && item.EventName == "FINISHED") { text = item.ToState; break; } } if (text != next) { return null; } Wait val = null; foreach (FsmStateAction item2 in (Il2CppArrayBase<FsmStateAction>)(object)state.Actions) { if (item2 == null || !item2.Enabled) { continue; } Wait val2 = ((Il2CppObjectBase)item2).TryCast<Wait>(); if (val2 != null) { if (val != null || val2.realTime || val2.time == null) { return null; } if (val2.finishEvent == null || val2.finishEvent.Name != "FINISHED") { return null; } val = val2; } } return val; } private static bool OnlySetFsmInt(FsmState s) { if (((s != null) ? s.Actions : null) == null) { return false; } foreach (FsmStateAction item in (Il2CppArrayBase<FsmStateAction>)(object)s.Actions) { if (item != null && item.Enabled && ((Il2CppObjectBase)item).TryCast<SetFsmInt>() == null) { return false; } } return true; } private void WarnShape(string what) { if (!_warnedShape) { _warnedShape = true; Core.Log.Warning($"[{Name}] {what} doesn't look as expected; leaving that wait vanilla"); } } } public class CursorGuardFeature : Feature { private sealed class Plan { public FsmState State; public List<SetFsmInt> Hides; } private sealed class Pending { public FsmState State; public Fsm Fsm; public readonly List<FsmInt> Values = new List<FsmInt>(); } private const string PlayerPath = "__SYSTEM/FPS Home/FPSController - Prince"; private const string PlayerObject = "FPSController - Prince"; private const string PlayerCamera = "FirstPersonCharacter"; private const string GmObject = "Global Manager"; private const string CursorVar = "NewCursor"; private const int Hidden = 0; private const int Crosshair = 2; private static readonly HashSet<string> PlayerFreeStates = new HashSet<string> { "State 1", "State 7", "State 14", "State 17" }; private static readonly HashSet<string> FreezeEvents = new HashSet<string> { "Freeze", "QuickFreeze", "InvisFreeze", "Freeze No Retract", "PumpFreeze", "Camfreeze", "disablemove", "disablemovefoundation", "CamTarget", "CamWarp" }; private static readonly HashSet<string> PlayerOnlyFreezeEvents = new HashSet<string> { "disable", "enable", "Update" }; private static readonly HashSet<string> ExcludingActions = new HashSet<string> { "SetMainCamera", "CameraFadeOut", "CameraFadeIn", "SetMouseCursor2" }; private MelonPreferences_Entry<bool> _log; private MelonPreferences_Entry<bool> _mainMenu; private const string MenuMouseManager = "MOUSE MANAGER"; private const string MenuIdleState = "Cursor Check"; private const int MenuPointer = 1; private float _menuUntil = -1f; private static readonly Plan Nothing = new Plan(); private readonly Dictionary<IntPtr, Plan> _plans = new Dictionary<IntPtr, Plan>(); private readonly Dictionary<IntPtr, bool> _fsmExcluded = new Dictionary<IntPtr, bool>(); private readonly Dictionary<IntPtr, Pending> _pending = new Dictionary<IntPtr, Pending>(); private readonly List<IntPtr> _tmp = new List<IntPtr>(); private Fsm _playerFsm; private float _nextPlayerSearch; private int _swaps; private bool _warnedPlan; public override string Name => "CursorGuard"; protected override string Description => "Keep the crosshair visible while you can move (objects that hide it during their own animation, e.g. Parlor box keyholes)"; protected override void CreatePrefs() { _mainMenu = Core.Prefs.CreateEntry<bool>("CursorGuard_MainMenu", true, "CursorGuard: main menu pointer", "Show the mouse pointer as soon as the main menu loads (the game hides it until the menu has faded in)", false, false, (ValueValidator)null, (string)null); _log = Core.Prefs.CreateEntry<bool>("CursorGuard_Log", false, "CursorGuard: log", "Log every hidden-cursor write that is turned into the crosshair (the first 3 are always logged)", false, false, (ValueValidator)null, (string)null); FsmHooks.BeforeEnter += OnBeforeEnter; } public override void OnSceneLoaded(string sceneName) { RestoreAll(); _plans.Clear(); _fsmExcluded.Clear(); _playerFsm = null; _nextPlayerSearch = 0f; _menuUntil = ((sceneName == "Main Menu") ? (Time.unscaledTime + 15f) : (-1f)); } private void OnBeforeEnter(FsmState state) { IntPtr pointer = ((Il2CppObjectBase)state).Pointer; if (_pending.Count > 0 && _pending.TryGetValue(pointer, out var value)) { Restore(pointer, value); } if (!base.Enabled) { if (_pending.Count > 0) { RestoreAll(); } return; } if (!_plans.TryGetValue(pointer, out var value2)) { try { value2 = BuildPlan(state); } catch (Exception ex) { value2 = Nothing; if (!_warnedPlan) { Core.Log.Warning($"[{Name}] could not analyse state '{state.Name}': {ex.Message}"); } _warnedPlan = true; } _plans[pointer] = value2; } if (value2 == Nothing) { return; } Fsm fsm = state.Fsm; if (fsm == null || !PlayerFree()) { return; } Pending pending = null; foreach (SetFsmInt hide in value2.Hides) { if (!((FsmStateAction)hide).Enabled) { continue; } FsmInt setValue = hide.setValue; if (setValue == null || setValue.Value != 0) { continue; } GameObject ownerDefaultTarget = fsm.GetOwnerDefaultTarget(hide.gameObject); if (!((Object)(object)ownerDefaultTarget == (Object)null) && !(((Object)ownerDefaultTarget).name != "Global Manager")) { setValue.Value = 2; object obj = pending; if (obj == null) { obj = new Pending { State = state, Fsm = fsm }; pending = (Pending)obj; } ((Pending)obj).Values.Add(setValue); } } if (pending != null) { _pending[pointer] = pending; if (_log.Value || _swaps < 3) { Core.Log.Msg($"[{Name}] keeping the crosshair: {FsmHooks.PathOf(fsm.GameObject)} :: {fsm.Name} :: {state.Name}"); } _swaps++; } } public override void OnUpdate() { if (_menuUntil >= 0f) { MenuPointerOnce(); } if (_pending.Count == 0) { return; } bool flag = PlayerFree(); _tmp.Clear(); foreach (KeyValuePair<IntPtr, Pending> item in _pending) { Pending value = item.Value; Fsm fsm = value.Fsm; FsmState val = ((fsm != null) ? fsm.ActiveState : null); if (!flag || val == null || ((Il2CppObjectBase)val).Pointer != ((Il2CppObjectBase)value.State).Pointer) { _tmp.Add(item.Key); } } foreach (IntPtr item2 in _tmp) { Restore(item2, _pending[item2]); } } private void MenuPointerOnce() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) if (!_mainMenu.Value || Time.unscaledTime > _menuUntil) { _menuUntil = -1f; return; } GameObject go = null; Scene activeScene = SceneManager.GetActiveScene(); foreach (GameObject item in (Il2CppArrayBase<GameObject>)(object)((Scene)(ref activeScene)).GetRootGameObjects()) { if ((Object)(object)item != (Object)null && ((Object)item).name == "MOUSE MANAGER") { go = item; break; } } PlayMakerFSM val = FsmUtil.Get(go); if ((Object)(object)val == (Object)null || val.ActiveStateName != "Cursor Check") { return; } _menuUntil = -1f; FsmInt val2 = FsmUtil.Int(val, "NewCursor"); FsmInt val3 = FsmUtil.Int(val, "CurrentCursor"); if (val2 != null && val3 != null && val2.Value == 0 && val3.Value == 0) { val2.Value = 1; if (_log.Value) { Core.Log.Msg("[" + Name + "] main menu pointer shown at once"); } } } private void Restore(IntPtr ptr, Pending p) { _pending.Remove(ptr); foreach (FsmInt value in p.Values) { try { if (value.Value == 2) { value.Value = 0; } } catch { } } } private void RestoreAll() { if (_pending.Count == 0) { return; } _tmp.Clear(); _tmp.AddRange(_pending.Keys); foreach (IntPtr item in _tmp) { Restore(item, _pending[item]); } } private bool PlayerFree() { Fsm val = PlayerFsm(); if (val == null) { return false; } string activeStateName = val.ActiveStateName; if (activeStateName == null || !PlayerFreeStates.Contains(activeStateName)) { return false; } FsmVariables variables = val.Variables; FsmBool val2 = ((variables != null) ? variables.GetFsmBool("currently frozen") : null); if (val2 == null || val2.Value) { return false; } Camera main = Camera.main; if ((Object)(object)main != (Object)null) { return ((Object)((Component)main).gameObject).name == "FirstPersonCharacter"; } return false; } private Fsm PlayerFsm() { if (_playerFsm != null) { return _playerFsm; } if (Time.unscaledTime < _nextPlayerSearch) { return null; } _nextPlayerSearch = Time.unscaledTime + 1f; PlayMakerFSM obj = FsmUtil.Find("__SYSTEM/FPS Home/FPSController - Prince"); return _playerFsm = ((obj != null) ? obj.Fsm : null); } private Plan BuildPlan(FsmState state) { Fsm fsm = state.Fsm; Il2CppReferenceArray<FsmStateAction> actions = state.Actions; if (fsm == null || actions == null) { return Nothing; } List<SetFsmInt> list = null; foreach (FsmStateAction item in (Il2CppArrayBase<FsmStateAction>)(object)actions) { if (item == null || !item.Enabled) { continue; } SetFsmInt val = ((Il2CppObjectBase)item).TryCast<SetFsmInt>(); if (val == null) { continue; } FsmString variableName = val.variableName; if (!(((variableName != null) ? variableName.Value : null) != "NewCursor") && (val.fsmName == null || string.IsNullOrEmpty(val.fsmName.Value) || !(val.fsmName.Value != "FSM"))) { FsmInt setValue = val.setValue; if (setValue != null && (!((NamedVariable)setValue).UseVariable || string.IsNullOrEmpty(((NamedVariable)setValue).Name)) && setValue.Value == 0) { (list ?? (list = new List<SetFsmInt>())).Add(val); } } } if (list == null || FsmExcluded(fsm)) { return Nothing; } return new Plan { State = state, Hides = list }; } private bool FsmExcluded(Fsm fsm) { IntPtr pointer = ((Il2CppObjectBase)fsm).Pointer; if (_fsmExcluded.TryGetValue(pointer, out var value)) { return value; } value = ComputeExcluded(fsm); _fsmExcluded[pointer] = value; return value; } private static bool ComputeExcluded(Fsm fsm) { GameObject gameObject = fsm.GameObject; if ((Object)(object)gameObject == (Object)null) { return true; } if (((Object)gameObject).name == "FPSController - Prince" || ((Object)gameObject).name == "Global Manager" || ((Object)gameObject).name == "DRAFT UI") { return true; } Transform val = gameObject.transform; while ((Object)(object)val != (Object)null) { if (((Object)val).name == "UI OVERLAY CAM") { return true; } val = val.parent; } Il2CppReferenceArray<FsmState> states = fsm.States; if (states == null) { return true; } foreach (FsmState item in (Il2CppArrayBase<FsmState>)(object)states) { if (((item != null) ? item.Actions : null) == null) { continue; } foreach (FsmStateAction item2 in (Il2CppArrayBase<FsmStateAction>)(object)item.Actions) { if (item2 == null || !item2.Enabled) { continue; } string name = ((MemberInfo)((Object)item2).GetIl2CppType()).Name; if (ExcludingActions.Contains(name)) { return true; } if (name != "SendEvent") { continue; } SendEvent val2 = ((Il2CppObjectBase)item2).Cast<SendEvent>(); FsmEvent sendEvent = val2.sendEvent; string text = ((sendEvent != null) ? sendEvent.Name : null); if (text != null) { if (FreezeEvents.Contains(text)) { return true; } if (PlayerOnlyFreezeEvents.Contains(text) && MaybePlayer(fsm, val2.eventTarget)) { return true; } } } } return false; } private static bool MaybePlayer(Fsm fsm, FsmEventTarget et) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Invalid comparison between Unknown and I4 if (et == null) { return false; } if ((int)et.target == 4) { return true; } if ((int)et.target != 1 && (int)et.target != 2) { return false; } GameObject ownerDefaultTarget; try { ownerDefaultTarget = fsm.GetOwnerDefaultTarget(et.gameObject); } catch { return true; } if (!((Object)(object)ownerDefaultTarget == (Object)null)) { return ((Object)ownerDefaultTarget).name.StartsWith("FPSController", StringComparison.Ordinal); } return true; } } public class DareGameOverFeature : Feature { private enum Kind { None, GameOverStart, MusicFade, MenuWait, MenuFadeUp, MenuReady, MenuExit } private sealed class Pending { public FsmState State; public readonly List<(FsmFloat f, float v)> Floats = new List<(FsmFloat, float)>(); } private const string GameOverObject = "GAME OVER"; private const string DareEngineObject = "DARE ENGINE"; private const string MenuObject = "Main Menu"; private const string SelectorObject = "Selector"; private const string FadeUpObject = "Fade up"; private const string WaitForFreeObject = "Wait for Free"; private const string VoPath = "DAY/Music/gameoverVO"; private const float FastFade = 1f; private const float MinDur = 0.05f; private const float SkipScale = 10f; private const float SkipGrace = 0.75f; private const float SkipTimeout = 15f; private const float VoFadeTime = 0.5f; private MelonPreferences_Entry<float> _menuDelay; private MelonPreferences_Entry<string> _skipKeysPref; private MelonPreferences_Entry<bool> _autoSkip; private readonly List<KeyCode> _skipKeys = new List<KeyCode>(); private readonly Dictionary<IntPtr, Kind> _kinds = new Dictionary<IntPtr, Kind>(); private readonly Dictionary<IntPtr, Pending> _pending = new Dictionary<IntPtr, Pending>(); private readonly List<IntPtr> _tmp = new List<IntPtr>(); private GameObject _gameOver; private float _gameOverAt; private bool _menuReached; private bool _skipping; private float _skipAt; private float _savedScale; private bool _muteVo; private AudioSource _vo; public override string Name => "DareGameOver"; protected override string Description => "Shorter Dare mode Game Over scene: Main Menu button sooner, and a key to fast-forward the poem"; protected override void CreatePrefs() { _menuDelay = Core.Prefs.CreateEntry<float>("DareGameOver_MenuDelay", 3f, "DareGameOver: menu delay", "Seconds after the Game Over screen appears before the Main Menu button shows (vanilla: 10)", false, false, (ValueValidator)null, (string)null); _skipKeysPref = Core.Prefs.CreateEntry<string>("DareGameOver_SkipKeys", "Space,Return,Escape,Mouse0", "DareGameOver: skip keys", "Keys that fast-forward the poem to the Game Over screen (UnityEngine.KeyCode names, comma separated; Mouse0 = left click). Empty = no skip key", false, false, (ValueValidator)null, (string)null); _autoSkip = Core.Prefs.CreateEntry<bool>("DareGameOver_AutoSkip", false, "DareGameOver: auto skip", "Always fast-forward the poem, without pressing a key", false, false, (ValueValidator)null, (string)null); ParseKeys(); ((MelonEventBase<LemonAction<string, string>>)(object)_skipKeysPref.OnEntryValueChanged).Subscribe((LemonAction<string, string>)delegate { ParseKeys(); }, 0, false); FsmHooks.BeforeEnter += OnBeforeEnter; FsmHooks.AfterEnter += OnAfterEnter; } private void ParseKeys() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) _skipKeys.Clear(); string[] array = (_skipKeysPref.Value ?? "").Split(new char[3] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { if (Enum.TryParse<KeyCode>(text.Trim(), ignoreCase: true, out KeyCode result) && (int)result != 0) { _skipKeys.Add(result); continue; } Core.Log.Warning($"[{Name}] unknown key '{text}' in DareGameOver_SkipKeys"); } } public override void OnSceneLoaded(string sceneName) { StopSkip("scene load"); RestoreAll(); _kinds.Clear(); _gameOver = null; _menuReached = false; _muteVo = false; _vo = null; } private void OnBeforeEnter(FsmState state) { IntPtr pointer = ((Il2CppObjectBase)state).Pointer; if (_pending.Count > 0) { if (_pending.TryGetValue(pointer, out var value)) { Restore(pointer, value); } if (!base.Enabled) { RestoreAll(); } } if (!_kinds.TryGetValue(pointer, out var value2)) { value2 = Classify(state); _kinds[pointer] = value2; } if (value2 == Kind.None || !base.Enabled) { return; } switch (value2) { case Kind.GameOverStart: { Fsm fsm = state.Fsm; _gameOver = ((fsm != null) ? fsm.GameObject : null); _gameOverAt = Time.realtimeSinceStartup; _menuReached = false; Core.Log.Msg("[" + Name + "] Game Over scene started"); if (_autoSkip.Value) { StartSkip(); } return; } case Kind.MenuReady: return; } Pending pending = new Pending { State = state }; List<string> list = new List<string>(); foreach (FsmStateAction item in (Il2CppArrayBase<FsmStateAction>)(object)state.Actions) { if (item == null || !item.Enabled) { continue; } switch (value2) { case Kind.MusicFade: { Wait val4 = ((Il2CppObjectBase)item).TryCast<Wait>(); if (val4 != null) { Shorten(pending, val4.time, 1f, "Wait", list); break; } TrackFadeToVolumeAction val5 = ((Il2CppObjectBase)item).TryCast<TrackFadeToVolumeAction>(); if (val5 != null) { Shorten(pending, val5.fadeTime, 1f, "music fade", list); } break; } case Kind.MenuWait: { Wait val6 = ((Il2CppObjectBase)item).TryCast<Wait>(); if (val6 != null) { Shorten(pending, val6.time, Math.Max(_menuDelay.Value, 0.05f), "Wait", list); } break; } case Kind.MenuFadeUp: { DotweenEaseColor val3 = ((Il2CppObjectBase)item).TryCast<DotweenEaseColor>(); if (val3 != null) { Shorten(pending, val3.duration, 1f, "fade up", list); } break; } case Kind.MenuExit: { CameraFadeOut val = ((Il2CppObjectBase)item).TryCast<CameraFadeOut>(); if (val != null) { Shorten(pending, val.time, 1f, "FadeOut", list); break; } DotweenAudioMixer val2 = ((Il2CppObjectBase)item).TryCast<DotweenAudioMixer>(); if (val2 != null) { Shorten(pending, val2.duration, 1f, "Audio", list); } break; } } } if (pending.Floats.Count != 0) { _pending[pointer] = pending; Instance log = Core.Log; DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(8, 4); defaultInterpolatedStringHandler.AppendLiteral("["); defaultInterpolatedStringHandler.AppendFormatted(Name); defaultInterpolatedStringHandler.AppendLiteral("] "); Fsm fsm2 = state.Fsm; object value3; if (fsm2 == null) { value3 = null; } else { GameObject gameObject = fsm2.GameObject; value3 = ((gameObject != null) ? ((Object)gameObject).name : null); } defaultInterpolatedStringHandler.AppendFormatted((string?)value3); defaultInterpolatedStringHandler.AppendLiteral(" / "); defaultInterpolatedStringHandler.AppendFormatted(state.Name); defaultInterpolatedStringHandler.AppendLiteral(": "); defaultInterpolatedStringHandler.AppendFormatted(string.Join(", ", list)); log.Msg(defaultInterpolatedStringHandler.ToStringAndClear()); } } private void OnAfterEnter(FsmState state) { if (_kinds.TryGetValue(((Il2CppObjectBase)state).Pointer, out var value) && value == Kind.MenuReady) { _menuReached = true; StopSkip("Main Menu button ready"); } } public override void OnUpdate() { //IL_015e: Unknown result type (might be due to invalid IL or missing references) if (_pending.Count > 0) { _tmp.Clear(); foreach (KeyValuePair<IntPtr, Pending> item in _pending) { bool flag; try { flag = item.Value.State.Active; } catch { flag = false; } if (!flag) { _tmp.Add(item.Key); } } foreach (IntPtr item2 in _tmp) { Restore(item2, _pending[item2]); } } if (_muteVo) { FadeVo(); } bool flag2 = (Object)(object)_gameOver != (Object)null && _gameOver.activeInHierarchy; if (_skipping) { if (!flag2) { StopSkip("Game Over scene ended"); } else if (Time.realtimeSinceStartup - _skipAt > 15f) { StopSkip("timeout"); } } else { if (!flag2 || _menuReached || _skipKeys.Count == 0 || Time.realtimeSinceStartup - _gameOverAt < 0.75f) { return; } foreach (KeyCode skipKey in _skipKeys) { if (Input.GetKeyDown(skipKey)) { StartSkip(); break; } } } } private void StartSkip() { if (!_skipping && !_menuReached) { _savedScale = Time.timeScale; Time.timeScale = 10f; _skipping = true; _skipAt = Time.realtimeSinceStartup; _muteVo = true; Core.Log.Msg("[" + Name + "] fast-forwarding to the Game Over screen"); } } private void StopSkip(string why) { if (_skipping) { _skipping = false; if (Mathf.Approximately(Time.timeScale, 10f)) { Time.timeScale = _savedScale; } Core.Log.Msg($"[{Name}] fast-forward stopped ({why}) after {Time.realtimeSinceStartup - _skipAt:0.0}s"); } } private void FadeVo() { if ((Object)(object)_vo == (Object)null) { GameObject val = GameObject.Find("DAY/Music/gameoverVO"); if ((Object)(object)val == (Object)null) { return; } _vo = val.GetComponent<AudioSource>(); if ((Object)(object)_vo == (Object)null) { _muteVo = false; return; } } if (_vo.isPlaying) { _vo.volume = Mathf.MoveTowards(_vo.volume, 0f, Time.unscaledDeltaTime * 0.24f / 0.5f); if (_vo.volume <= 0f) { _vo.Stop(); } } } private static void Shorten(Pending pend, FsmFloat v, float target, string what, List<string> log) { if (IsConst(v)) { float value = v.Value; if (!(value <= target)) { pend.Floats.Add((v, value)); v.Value = target; log.Add($"{what} {value:0.##}s -> {target:0.##}s"); } } } private void Restore(IntPtr ptr, Pending p) { _pending.Remove(ptr); foreach (var (val, value) in p.Floats) { try { val.Value = value; } catch { } } } private void RestoreAll() { if (_pending.Count == 0) { return; } _tmp.Clear(); _tmp.AddRange(_pending.Keys); foreach (IntPtr item in _tmp) { Restore(item, _pending[item]); } } private static Kind Classify(FsmState state) { Fsm fsm = state.Fsm; if (fsm == null || fsm.Name != "FSM") { return Kind.None; } GameObject gameObject = fsm.GameObject; if ((Object)(object)gameObject == (Object)null) { return Kind.None; } Transform parent = gameObject.transform.parent; string text = (((Object)(object)parent != (Object)null) ? ((Object)parent).name : null); string name = state.Name; switch (((Object)gameObject).name) { case "GAME OVER": if (text == "DARE ENGINE") { if (!(name == "pre fade 7")) { return Kind.None; } return Kind.GameOverStart; } break; case "Wait for Free": if (text == "DARE ENGINE") { if (!(name == "State 4")) { return Kind.None; } return Kind.MusicFade; } break; case "Main Menu": if (text == "Selector") { return name switch { "State 1" => Kind.MenuWait, "State 3" => Kind.MenuReady, "State 6" => Kind.MenuExit, _ => Kind.None, }; } break; case "Fade up": if (text == "Main Menu") { if (!(name == "State 2")) { return Kind.None; } return Kind.MenuFadeUp; } break; } return Kind.None; } private static bool IsConst(FsmFloat f) { if (f != null) { if (((NamedVariable)f).UseVariable) { return string.IsNullOrEmpty(((NamedVariable)f).Name); } return true; } return false; } } public class DartsEquationFeature : Feature { private readonly struct Sector { public readonly int Number; public readonly char Ring; public readonly string Type; public Sector(int n, char ring, string type) { Number = n; Ring = ring; Type = type; } } private const string Collider = "Darts - First Collider"; private const string BoardPath = "Dartboard New"; private static readonly Regex SectorRe = new Regex("^(\\d+)([A-Z])(.+)$"); private MelonPreferences_Entry<float> _fontScale; private MelonPreferences_Entry<float> _posY; private MelonPreferences_Entry<string> _fontPref; private MelonPreferences_Entry<bool> _redOdd; private const string RampProp = "_RampColor2"; private static readonly Color OddTint = new Color(1f, 0.7f, 0.7f, 1f); private readonly List<Renderer> _numbers = new List<Renderer>(); private readonly HashSet<IntPtr> _numberSeen = new HashSet<IntPtr>(); private bool _tinted; private MaterialPropertyBlock _block; private GameObject _collider; private DartsStageManager _board; private string _raw; private string _text = ""; private GameObject _root; private TextMeshProUGUI _tmp; private RectTransform _panel; private string _layoutText; private float _layoutSize; private float _layoutWidth; private int _layoutLines = 1; private bool _gaveUp; private bool _menuCheckBroken; private int _attempts; private float _nextTry; private string _shownText; private string _times = "×"; private string _divide = "÷"; private string _colAdd = "#76CEE5"; private string _colSub = "#FFF51E"; private string _colMul = "#B47097"; private string _colDiv = "#7E69C5"; private const string ColCrossed = "#9A9A9A"; private static readonly Dictionary<string, string[]> Shapes = new Dictionary<string, string[]> { ["S"] = new string[1] { "sq" }, ["D"] = new string[1] { "sq4" }, ["V"] = new string[1] { "rev" }, ["O"] = new string[1] { "r1" }, ["T"] = new string[1] { "r10" }, ["H"] = new string[1] { "r100" }, ["W"] = new string[2] { "sq", "rev" }, ["Q"] = new string[2] { "rev", "sq" }, ["1"] = new string[2] { "r1", "sq" }, ["2"] = new string[2] { "r10", "sq" }, ["3"] = new string[2] { "r100", "sq" }, ["4"] = new string[2] { "rev", "r1" }, ["5"] = new string[2] { "rev", "r10" }, ["6"] = new string[2] { "rev", "r100" }, ["7"] = new string[2] { "r1", "rev" }, ["8"] = new string[2] { "r10", "rev" }, ["9"] = new string[2] { "r100", "rev" }, ["A"] = new string[2] { "sq", "r1" }, ["B"] = new string[2] { "sq", "r10" }, ["C"] = new string[2] { "sq", "r100" } }; public override string Name => "DartsEquation"; protected override string Description => "Shows the Billiard Room dartboard puzzle as an equation at the bottom of the screen, and tints the board's odd numbers red"; protected override bool DefaultEnabled => false; protected override void CreatePrefs() { MelonPreferences_Category prefs = Core.Prefs; _fontScale = prefs.CreateEntry<float>(Name + "_FontScale", 0.045f, "DartsEquation: font scale", "Text size as a fraction of screen height.", false, false, (ValueValidator)null, (string)null); _posY = prefs.CreateEntry<float>(Name + "_PosY", 0.935f, "DartsEquation: position", "Vertical position of the equation as a fraction of the screen height from the top.", false, false, (ValueValidator)null, (string)null); _fontPref = prefs.CreateEntry<string>(Name + "_Font", "TypeNeat SDF", "DartsEquation: font", "A game TextMeshPro font asset name. The default has the × and ÷ signs; other fonts fall back to x and /.", false, false, (ValueValidator)null, (string)null); _redOdd = prefs.CreateEntry<bool>(Name + "_RedOddNumbers", true, "DartsEquation: red odd numbers", "Tints the odd numbers around the Billiard Room dartboard slightly red.", false, false, (ValueValidator)null, (string)null); FsmHooks.AfterEnter += OnAfterEnter; } public override void OnSceneLoaded(string sceneName) { _collider = null; _board = null; _raw = null; _numbers.Clear(); _numberSeen.Clear(); _tinted = false; } private void OnAfterEnter(FsmState state) { Fsm fsm = state.Fsm; GameObject val = ((fsm != null) ? fsm.GameObject : null); if ((Object)(object)val == (Object)null) { return; } if (state.Fsm.Name == "Billiards") { SeeNumber(val); } else if (!(((Object)val).name != "Darts - First Collider") && !(state.Fsm.Name != "FSM")) { string name = state.Name; bool flag = ((name == "Camera Move 2" || name == "Wait for Exit 2") ? true : false); bool flag2 = flag; if (flag2 && (Object)(object)_collider != (Object)(object)val) { _board = null; _raw = null; } _collider = (flag2 ? val : null); } } private void SeeNumber(GameObject go) { if (!_numberSeen.Add(((Il2CppObjectBase)go).Pointer)) { return; } Transform parent = go.transform.parent; if ((Object)(object)parent == (Object)null || (Object)(object)parent.parent == (Object)null || ((Object)parent.parent).name != "Dartboard System" || !((Object)parent).name.EndsWith("holder", StringComparison.Ordinal) || !int.TryParse(((Object)go).name, out var result) || result % 2 == 0) { return; } Renderer component = go.GetComponent<Renderer>(); if (!((Object)(object)component == (Object)null)) { _numbers.Add(component); if (_tinted) { Tint(component, on: true); } } } private void SyncTint() { bool flag = base.Enabled && _redOdd.Value; if (flag == _tinted) { return; } _tinted = flag; foreach (Renderer number in _numbers) { Tint(number, flag); } } private void Tint(Renderer r, bool on) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) try { if (!((Object)(object)r == (Object)null)) { if (_block == null) { _block = new MaterialPropertyBlock(); } r.GetPropertyBlock(_block); Material sharedMaterial = r.sharedMaterial; Color val = (((Object)(object)sharedMaterial != (Object)null && sharedMaterial.HasProperty("_RampColor2")) ? sharedMaterial.GetColor("_RampColor2") : Color.white); _block.SetColor("_RampColor2", on ? (val * OddTint) : val); r.SetPropertyBlock(_block); } } catch (Exception ex) { Core.Log.Warning("[" + Name + "] can't tint a dartboard number: " + ex.Message); } } public override void OnUpdate() { SyncTint(); bool show = base.Enabled && (Object)(object)_collider != (Object)null && _collider.activeInHierarchy && !MenuOpen() && UpdateText(); Sync(show); } private bool UpdateText() { if ((Object)(object)_board == (Object)null) { Transform val = (((Object)(object)_collider.transform.parent != (Object)null) ? _collider.transform.parent.Find("Dartboard New") : null); _board = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<DartsStageManager>() : null); if ((Object)(object)_board == (Object)null) { return false; } ReadColors(); Transform parent = _collider.transform.parent; for (int i = 0; i < parent.childCount; i++) { Transform child = parent.GetChild(i); if (child.childCount > 0 && ((Object)child).name.EndsWith("holder", StringComparison.Ordinal)) { SeeNumber(((Component)child.GetChild(0)).gameObject); } } } DartsPuzzleData currentStage = _board.CurrentStage; string text = ((currentStage != null) ? currentStage.RawString : null); if (string.IsNullOrEmpty(text)) { return false; } if (text != _raw) { _raw = text; try { _text = Build(text); } catch (Exception ex) { _text = ""; Core.Log.Warning($"[{Name}] can't read dart puzzle '{text}': {ex.Message}"); } } return _text.Length > 0; } private void ReadColors() { try { DartsColorData val = (((Object)(object)_board.TargetGraphics != (Object)null) ? _board.TargetGraphics.DartsColors : null); if (!((Object)(object)val == (Object)null)) { _colAdd = Hex(val.Color_Add, _colAdd); _colSub = Hex(val.Color_Subtract, _colSub); _colMul = Hex(val.Color_Multiply, _colMul); _colDiv = Hex(val.Color_Divide, _colDiv); } } catch (Exception ex) { Core.Log.Warning("[" + Name + "] darts colours not found, using defaults: " + ex.Message); } } private static string Hex(ColorData d, string fallback) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)d == (Object)null) { return fallback; } Color displayColor = d.DisplayColor; return $"#{Byte(displayColor.r):X2}{Byte(displayColor.g):X2}{Byte(displayColor.b):X2}"; static int Byte(float v) { return Mathf.Clamp(Mathf.RoundToInt(v * 255f), 0, 255); } } private string Build(string raw) { string[] array = raw.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if (array.Length < 2) { return ""; } string text = array[0]; char c = ((text.Length >= 3) ? text[2] : 'B'); string shape = ((text.Length >= 4) ? text.Substring(3) : "E"); Dictionary<int, string> dictionary = new Dictionary<int, string>(); SortedDictionary<char, List<Sector>> sortedDictionary = new SortedDictionary<char, List<Sector>>(); for (int i = 1; i < array.Length; i++) { Match match = SectorRe.Match(array[i]); if (!match.Success) { throw new FormatException("sector '" + array[i] + "'"); } Sector item = new Sector(int.Parse(match.Groups[1].Value), match.Groups[2].Value[0], match.Groups[3].Value); if (item.Ring == 'E') { dictionary[item.Number] = item.Type; continue; } if (!sortedDictionary.TryGetValue(item.Ring, out var value)) { value = (sortedDictionary[item.Ring] = new List<Sector>()); } value.Add(item); } StringBuilder stringBuilder = new StringBuilder(); bool flag = true; foreach (List<Sector> value5 in sortedDictionary.Values) { bool flag2 = false; foreach (Sector item2 in value5) { var (value2, value3, c2) = Op(item2.Type); dictionary.TryGetValue(item2.Number, out var value4); string text2 = Number(item2.Number, value4); string type = item2.Type; if ((type == "3" || type == "1") ? true : false) { text2 += "/3"; } StringBuilder stringBuilder2 = new StringBuilder(); bool flag3 = flag; if (flag3) { bool flag4 = ((c2 == 'D' || c2 == 'M') ? true : false); flag3 = flag4; } if (flag3) { stringBuilder2.Append("0 "); } if (!flag) { stringBuilder2.Append(' '); } StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(16, 2, stringBuilder3); handler.AppendLiteral("<color="); handler.AppendFormatted(value3); handler.AppendLiteral(">"); handler.AppendFormatted(value2); handler.AppendLiteral("</color>"); stringBuilder3.Append(ref handler); if (!flag) { stringBuilder2.Append(' '); } stringBuilder2.Append(text2); flag = false; if (value4 == "X") { stringBuilder.Append("<color=#9A9A9A><s>").Append(StripColors(stringBuilder2.ToString())).Append("</s></color>"); continue; } stringBuilder.Append(stringBuilder2); if (c2 == c) { flag2 = true; } } if (flag2) { Bullseye(stringBuilder, shape, ColorOf(c)); } } return stringBuilder.ToString(); } private (string sign, string color, char group) Op(string type) { switch (type) { case "A": case "3": return (sign: "+", color: _colAdd, group: 'A'); case "S": case "1": return (sign: "-", color: _colSub, group: 'S'); case "M": return (sign: _times, color: _colMul, group: 'M'); case "D": return (sign: _divide, color: _colDiv, group: 'D'); default: return (sign: "?", color: "#9A9A9A", group: '?'); } } private string ColorOf(char group) { return group switch { 'A' => _colAdd, 'S' => _colSub, 'M' => _colMul, 'D' => _colDiv, _ => "#9A9A9A", }; } private string Number(int n, string mark) { switch (mark) { case "W": return $"({n}{_times}2)"; case "H": return $"({n}/2)"; case "V": return $"rev({n})"; case null: case "X": return n.ToString(); default: return $"{n}<sup>?</sup>"; } } private static string StripColors(string s) { return Regex.Replace(s, "</?color[^>]*>", ""); } private unsafe static void Bullseye(StringBuilder sb, string shape, string color) { if (!(shape == "E")) { if (!Shapes.TryGetValue(shape, out var value)) { value = new string[1] { "?" }; } string[] array = value; for (int i = 0; i < array.Length; i++) { object obj = array[i] switch { "sq" => ("(", ")<sup>2</sup>"), "sq4" => ("(", ")<sup>4</sup>"), "rev" => ("rev(", ")"), "r1" => ("round(", ")"), "r10" => ("round<sub>10</sub>(", ")"), "r100" => ("round<sub>100</sub>(", ")"), _ => ("(", ")<sup>?</sup>"), }; string item = ((ValueTuple<string, string>*)(&obj))->Item1; string item2 = ((ValueTuple<string, string>*)(&obj))->Item2; StringBuilder stringBuilder = sb.Insert(0, $"<color={color}>{item}</color>"); StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(16, 2, stringBuilder); handler.AppendLiteral("<color="); handler.AppendFormatted(color); handler.AppendLiteral(">"); handler.AppendFormatted(item2); handler.AppendLiteral("</color>"); stringBuilder.Append(ref handler); } } } private bool MenuOpen() { if (_menuCheckBroken) { return false; } try { return UIScreen.AnyOpened; } catch (Exception ex) { _menuCheckBroken = true; Core.Log.Warning("[" + Name + "] menu detection failed, disabling it: " + ex.Message); return false; } } private void Sync(bool show) { //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_tmp == (Object)null) { if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); _root = null; } if (!show || _gaveUp || Time.unscaledTime < _nextTry) { return; } _nextTry = Time.unscaledTime + 1f; if (!TryCreate()) { return; } } if (!show) { if (_root.activeSelf) { _root.SetActive(false); } return; } if (!_root.activeSelf) { _root.SetActive(true); } if (_shownText != _text) { ((TMP_Text)_tmp).text = _text; _shownText = _text; } float num = Mathf.Max(10f, (float)Screen.height * Mathf.Clamp(_fontScale.Value, 0.01f, 0.2f)); ((TMP_Text)_tmp).fontSize = num; float num2 = num * 0.6f; if (_layoutText != _text || !Mathf.Approximately(_layoutSize, num)) { _layoutText = _text; _layoutSize = num; Vector2 preferredValues = ((TMP_Text)_tmp).GetPreferredValues(_text, (float)Screen.width * 0.9f, 0f); float y = ((TMP_Text)_tmp).GetPreferredValues("+0", 0f, 0f).y; _layoutWidth = Mathf.Min(preferredValues.x, (float)Screen.width * 0.9f); _layoutLines = ((!(y > 0f)) ? 1 : Mathf.Max(1, Mathf.RoundToInt(preferredValues.y / y))); } float layoutWidth = _layoutWidth; float num3 = (float)_layoutLines * num * 0.95f + num * 0.3f; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0.5f, 1f - Mathf.Clamp01(_posY.Value)); _panel.anchorMin = val; _panel.anchorMax = val; _panel.pivot = new Vector2(0.5f, 0.5f); _panel.anchoredPosition = Vector2.zero; _panel.sizeDelta = new Vector2(layoutWidth + 2f * num2, num3); RectTransform rectTransform = ((TMP_Text)_tmp).rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.offsetMin = new Vector2(num2, 0f); rectTransform.offsetMax = new Vector2(0f - num2, 0f); } private bool TryCreate() { //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Expected O, but got Unknown //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Expected O, but got Unknown //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Expected O, but got Unknown //IL_023a: Unknown result type (might be due to invalid IL or missing references) string value = (_fontPref.Value ?? "").Trim(); TMP_FontAsset val = null; TMP_FontAsset val2 = null; foreach (TMP_FontAsset item in Resources.