Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of PitSave v1.1.0
plugins/PitSave.dll
Decompiled 17 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.SceneManagement; [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("PitSave")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("PitSave")] [assembly: AssemblyTitle("PitSave")] [assembly: AssemblyVersion("1.0.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 YanPitSave { internal static class Cfg { public static ConfigEntry<bool> Enabled; public static ConfigEntry<float> PitY; public static ConfigEntry<float> SafeMinY; public static ConfigEntry<float> SafeMaxY; public static ConfigEntry<float> LiftY; public static ConfigEntry<float> SaveCooldown; public static ConfigEntry<float> FallDepth; public static ConfigEntry<float> BounceSpeed; public static ConfigEntry<float> PushInterval; public static ConfigEntry<float> PushRise; public static ConfigEntry<bool> NativePitBounce; public static ConfigEntry<bool> IncludeEnemyValuable; public static ConfigEntry<bool> ShowToast; public static ConfigEntry<float> ScanInterval; public static ConfigEntry<bool> DebugLog; private static AcceptableValueBase F(float min, float max) { return (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max); } public static void Bind(ConfigFile c) { Lang.Init(c, delegate(string s) { if (Plugin.Log != null) { Plugin.Log.LogInfo((object)s); } }); Enabled = Lang.B(c, "en", def: true); PitY = Lang.B(c, "pity", -25f, F(-80f, -10f)); SafeMinY = Lang.B(c, "safemin", -2f, F(-20f, 10f)); SafeMaxY = Lang.B(c, "safemax", 60f, F(10f, 150f)); LiftY = Lang.B(c, "lift", 1.5f, F(0f, 8f)); SaveCooldown = Lang.B(c, "cooldown", 2f, F(0f, 10f)); FallDepth = Lang.B(c, "falldepth", 4f, F(1f, 20f)); BounceSpeed = Lang.B(c, "bounce", 40f, F(2f, 60f)); PushInterval = Lang.B(c, "pushint", 0.35f, F(0.1f, 2f)); PushRise = Lang.B(c, "pushrise", 2f, F(0.3f, 3f)); NativePitBounce = Lang.B(c, "native", def: true); IncludeEnemyValuable = Lang.B(c, "enemyval", def: true); ShowToast = Lang.B(c, "toast", def: true); ScanInterval = Lang.B(c, "scan", 0.15f, F(0.05f, 1f)); DebugLog = Lang.B(c, "dbg", def: false); if (Mathf.Approximately(BounceSpeed.Value, 14f)) { BounceSpeed.Value = 40f; } if (Mathf.Approximately(PushRise.Value, 1.25f)) { PushRise.Value = 2f; } Lang.PurgeOther(c); } } internal static class Lang { public const string SecLang = "0.Language"; public const string KeyLang = "Language"; public static ConfigFile Cfg; public static ConfigEntry<string> E; public static bool En; private static Action<string> _out; private static float _uiAt; private const string SA = "1.掉坑救回"; private const string SB = "1. Pit rescue"; private const string SC = "3.显示与调试"; private const string SD = "3. Display"; private static readonly string[][] T; public static readonly string[] Ids; private static FieldInfo _fEntries; static Lang() { T = new string[15][] { new string[7] { "en", "1.掉坑救回", "1. Pit rescue", "启用掉落救回", "Enable rescue", "值钱物掉出地图时把它捞回来,不让它凭空消失。", "Valuables that fall out of the map are brought back instead of vanishing." }, new string[7] { "pity", "1.掉坑救回", "1. Pit rescue", "判定掉坑高度", "Pit Y threshold", "世界 Y 低于这个值就算掉出地图。游戏本体在 -50 才销毁,这里留了缓冲。", "World Y below this counts as fallen out of the map. The game itself only destroys things at -50, so there is room to spare." }, new string[7] { "safemin", "1.掉坑救回", "1. Pit rescue", "安全位置下限", "Safe min Y", "只有高于这个高度才记安全点,防止把坑里的位置当成安全点。", "Only record a safe spot above this height, so a position inside the pit is never taken as safe." }, new string[7] { "safemax", "1.掉坑救回", "1. Pit rescue", "安全位置上限", "Safe max Y", "高于这个高度不记录,防止飞在天上的位置被当成安全点。", "Nothing above this height is recorded, so a spot up in the air is never taken as safe." }, new string[7] { "lift", "1.掉坑救回", "1. Pit rescue", "救回抬高", "Rescue lift", "米。救回时在安全位置上再抬这么高,避免卡进地面。", "Meters. Raised this much above the safe spot on rescue, so it does not clip into the floor." }, new string[7] { "cooldown", "1.掉坑救回", "1. Pit rescue", "同物品冷却", "Same-item cooldown", "秒。同一件东西两次救回之间的最短间隔,防止来回弹。", "Seconds. Shortest gap between two rescues of the same object, so it cannot ping-pong." }, new string[7] { "falldepth", "1.掉坑救回", "1. Pit rescue", "掉坑判定落差", "Fall depth", "米。比记录的安全点低这么多而且还在往下掉,就算掉坑。调大更容易忽略小落差。", "Meters. This far below the recorded safe spot and still falling counts as fallen. Raise it to ignore small drops." }, new string[7] { "bounce", "1.掉坑救回", "1. Pit rescue", "弹回水平速度", "Horizontal bounce speed", "米/秒。掉坑后朝安全点方向弹出去的水平速度,填多少就是多少,不再打折。2-60。", "m/s. Horizontal speed fired back toward the safe spot after falling. The number is the speed itself, no scaling. 2-60." }, new string[7] { "pushint", "1.掉坑救回", "1. Pit rescue", "坑内续推间隔秒", "Re-push interval", "秒。掉坑后每隔这么久再给一次推力,直到它自己爬出来。0.1-2。", "Seconds. Another push is given this often while it is still down there, until it climbs out. 0.1-2." }, new string[7] { "pushrise", "1.掉坑救回", "1. Pit rescue", "抬升力度倍率", "Rise multiplier", "倍。往上弹的速度按坑深自动算,这里是倍率:1 差不多刚好,2 弹得高一截。0.3-3。", "Multiplier. The upward speed is derived from how deep it fell; this scales it. 1 is about right, 2 throws it clearly higher. 0.3-3." }, new string[7] { "native", "1.掉坑救回", "1. Pit rescue", "掉坑时闪原生特效", "Native pit effect", "掉坑那一刻点亮和死头一样的绿色烟雾/电弧,落地或被拿起就熄。", "Flashes the game's own green smoke / arc effect on the fall, same as the death head. Goes out on landing or pickup." }, new string[7] { "enemyval", "3.显示与调试", "3. Display", "含怪物掉落物", "Include enemy valuables", "怪物掉的值钱物也算。", "Valuables dropped by monsters count too." }, new string[7] { "toast", "3.显示与调试", "3. Display", "救回时屏幕提示", "Screen toast", "救回一件东西时屏幕角落显示一行字。", "Show a one-line message in the corner of the screen when something is saved." }, new string[7] { "scan", "3.显示与调试", "3. Display", "扫描间隔秒", "Scan interval", "秒。每帧扫很费,0.15 秒一次已经足够。0.05-1。", "Seconds. Scanning every frame is wasteful; once per 0.15s is plenty. 0.05-1." }, new string[7] { "dbg", "3.显示与调试", "3. Display", "调试日志", "Debug log", "开 = 往控制台打详细日志,排查用。", "On = verbose logging to the console, for troubleshooting." } }; Ids = new string[T.Length]; for (int i = 0; i < T.Length; i++) { Ids[i] = T[i][0]; } } private static int ix(bool en, int zh, int enIx) { if (!en) { return zh; } return enIx; } public static string Sec(string id, bool en) { return Row(id)[ix(en, 1, 2)]; } public static string Key(string id, bool en) { return Row(id)[ix(en, 3, 4)]; } public static string Desc(string id, bool en) { return Row(id)[ix(en, 5, 6)]; } private static string[] Row(string id) { for (int i = 0; i < T.Length; i++) { if (T[i][0] == id) { return T[i]; } } return T[0]; } internal static void Log(string m) { try { if (_out != null) { _out(m); } } catch { } } public static void Init(ConfigFile c, Action<string> log) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown Cfg = c; _out = log; E = c.Bind<string>("0.Language", "Language", "中文", new ConfigDescription("界面语言 / Interface language(改完立即生效,不用重启 / applies immediately, no restart needed)", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "中文", "English" }), Array.Empty<object>())); En = E.Value == "English"; } public static void Hook() { if (E != null) { E.SettingChanged += OnLangChanged; } } private static void OnLangChanged(object sender, EventArgs e) { //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Expected O, but got Unknown try { bool flag = E != null && E.Value == "English"; if (flag == En) { return; } En = flag; ConfigFile cfg = Cfg; int num = 0; int num2 = 0; foreach (DictionaryEntry item in EntriesDict(cfg)) { object? value = item.Value; ConfigEntryBase val = (ConfigEntryBase)((value is ConfigEntryBase) ? value : null); if (val == null || (object)val == E) { continue; } string text = IdOf(val.Definition); if (text == null) { num2++; continue; } try { SetBacking(val, "Definition", typeof(ConfigDefinition), (object)new ConfigDefinition(Sec(text, flag), Key(text, flag))); SetBacking(val, "Description", typeof(ConfigDescription), BuildDesc(text, flag, val.Description)); num++; } catch (Exception ex) { Log("relabel " + text + " fail: " + ex.Message); } } bool flag2 = RebuildEntries(cfg); try { cfg.Save(); } catch (Exception ex2) { Log("save fail: " + ex2.Message); } PurgeOther(cfg); Log("switch -> " + (flag ? "English" : "Chinese") + " relabeled=" + num + " skipped=" + num2 + " dict=" + (flag2 ? "ok" : "FAIL")); _uiAt = Time.time + 0.15f; } catch (Exception ex3) { Log("switch fail: " + ex3); } } public static void RefreshTick() { if (!(_uiAt <= 0f) && !(Time.time < _uiAt)) { _uiAt = 0f; RefreshMenu(); } } private static void RefreshMenu() { try { Type type = FindType("MenuLib.MenuAPI"); Type type2 = FindType("REPOConfig.ConfigMenu"); if (type2 == null) { Log("menu: ConfigMenu not found"); return; } FieldInfo field = type2.GetField("hasPopupMenuOpened", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); bool flag = false; try { flag = field != null && field.GetValue(null) is bool && (bool)field.GetValue(null); } catch { } if (!flag) { Log("menu closed, skip refresh"); return; } if (type != null) { MethodInfo method = type.GetMethod("CloseAllPagesAddedOnTop", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { method.Invoke(null, DefArgs(method)); } } MethodInfo method2 = type2.GetMethod("CreateModMenu", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (method2 != null && method2.GetParameters().Length == 0) { method2.Invoke(null, null); } Log("menu rebuilt"); } catch (Exception ex) { Log("menu refresh fail: " + ex.Message); } } private static object[] DefArgs(MethodInfo m) { ParameterInfo[] parameters = m.GetParameters(); object[] array = new object[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { Type parameterType = parameters[i].ParameterType; if (parameterType == typeof(bool)) { array[i] = true; } else if (parameterType.IsValueType) { array[i] = Activator.CreateInstance(parameterType); } else { array[i] = null; } } return array; } private static Type FindType(string fullName) { try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (!(assembly == null)) { Type type = assembly.GetType(fullName, throwOnError: false); if (type != null) { return type; } } } } catch { } return null; } private static string IdOf(ConfigDefinition d) { if (d == (ConfigDefinition)null) { return null; } for (int i = 0; i < Ids.Length; i++) { string text = Ids[i]; if ((!(d.Key != Key(text, en: true)) || !(d.Key != Key(text, en: false))) && (d.Section == Sec(text, en: true) || d.Section == Sec(text, en: false))) { return text; } } return null; } private static ConfigDescription BuildDesc(string id, bool en, ConfigDescription old) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown string text = Desc(id, en); AcceptableValueBase val = ((old != null) ? old.AcceptableValues : null); object[] array = ((old != null) ? old.Tags : null); if (val == null && (array == null || array.Length == 0)) { return new ConfigDescription(text, (AcceptableValueBase)null, Array.Empty<object>()); } return new ConfigDescription(text, val, (array == null) ? new object[0] : array); } private static void SetBacking(object entry, string hint, Type fieldType, object value) { Type type = entry.GetType(); while (type != null) { FieldInfo[] fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (!(fieldInfo.FieldType != fieldType) && fieldInfo.Name.IndexOf(hint, StringComparison.Ordinal) >= 0) { fieldInfo.SetValue(entry, value); return; } } type = type.BaseType; } throw new Exception(hint + " backing field not found"); } private static FieldInfo EntriesField(ConfigFile c) { if (_fEntries != null) { return _fEntries; } Type typeFromHandle = typeof(ConfigFile); FieldInfo fieldInfo = typeFromHandle.GetField("<Entries>k__BackingField", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (fieldInfo == null) { FieldInfo[] fields = typeFromHandle.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); for (int i = 0; i < fields.Length; i++) { if (typeof(IDictionary).IsAssignableFrom(fields[i].FieldType)) { fieldInfo = fields[i]; break; } } } _fEntries = fieldInfo; return fieldInfo; } private static IDictionary EntriesDict(ConfigFile c) { FieldInfo fieldInfo = EntriesField(c); if (fieldInfo == null) { return new Hashtable(); } if (!(fieldInfo.GetValue(c) is IDictionary result)) { return new Hashtable(); } return result; } private static bool RebuildEntries(ConfigFile c) { FieldInfo fieldInfo = EntriesField(c); if (fieldInfo == null) { return false; } try { if (!(fieldInfo.GetValue(c) is IDictionary dictionary)) { return false; } IDictionary dictionary2 = (IDictionary)Activator.CreateInstance(dictionary.GetType()); foreach (DictionaryEntry item in dictionary) { object? value = item.Value; ConfigEntryBase val = (ConfigEntryBase)((value is ConfigEntryBase) ? value : null); if (val != null) { dictionary2[val.Definition] = val; } } fieldInfo.SetValue(c, dictionary2); return dictionary2.Count == dictionary.Count; } catch (Exception ex) { Log("rebuild entries fail " + ex.Message); return false; } } public static ConfigEntry<T> B<T>(ConfigFile c, string id, T def, AcceptableValueBase range = null) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown ConfigDefinition val = new ConfigDefinition(Sec(id, En), Key(id, En)); ConfigDefinition key = new ConfigDefinition(Sec(id, !En), Key(id, !En)); string text = null; IDictionary dictionary = Orphans(c); if (dictionary != null) { if (dictionary.Contains(val)) { text = dictionary[val] as string; } else if (dictionary.Contains(key)) { text = dictionary[key] as string; } } ConfigDescription val2 = ((range != null) ? new ConfigDescription(Desc(id, En), range, Array.Empty<object>()) : new ConfigDescription(Desc(id, En), (AcceptableValueBase)null, Array.Empty<object>())); ConfigEntry<T> val3 = c.Bind<T>(val, def, val2); if (!string.IsNullOrEmpty(text)) { try { ((ConfigEntryBase)val3).SetSerializedValue(text); } catch (Exception ex) { Log("migrate " + id + " fail: " + ex.Message); } } return val3; } public static void PurgeOther(ConfigFile c) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown IDictionary dictionary = Orphans(c); if (dictionary == null) { return; } bool flag = false; for (int i = 0; i < Ids.Length; i++) { string id = Ids[i]; ConfigDefinition key = new ConfigDefinition(Sec(id, !En), Key(id, !En)); if (dictionary.Contains(key)) { dictionary.Remove(key); flag = true; } } ArrayList arrayList = new ArrayList(); foreach (DictionaryEntry item in dictionary) { object key2 = item.Key; ConfigDefinition val = (ConfigDefinition)((key2 is ConfigDefinition) ? key2 : null); if (!(val == (ConfigDefinition)null) && val.Section != null && val.Section.StartsWith("2.旧无敌")) { arrayList.Add(val); } } for (int j = 0; j < arrayList.Count; j++) { dictionary.Remove(arrayList[j]); flag = true; } if (!flag) { return; } try { c.Save(); } catch (Exception ex) { Log("purge save fail " + ex.Message); } } private static IDictionary Orphans(ConfigFile c) { try { PropertyInfo property = typeof(ConfigFile).GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property == null) { return null; } return property.GetValue(c, null) as IDictionary; } catch { return null; } } } internal class Tracked { public Component Pgo; public Vector3 SafePos; public float SafeAt; public float LastGrab; public float NextPush; public bool Guarded; public bool OrigDisable; public float LastSave; public float LastPit; public float CalmAt; public bool HasSafe; public bool WasHeld; public int Saves; public float MinY = 9999f; public string Label = "item"; } internal class FxState { public GameObject Go; public Component Eff; public ParticleSystem[] Ps; public float Until; public float NextReset; } internal static class Pit { private static readonly Dictionary<int, Tracked> _map = new Dictionary<int, Tracked>(); private static readonly List<int> _gone = new List<int>(); private static float _nextScan; private static float _nextClean; private static Type TPgo; private static Type TVal; private static Type TEnemyVal; private static Type TEffect; private static MethodInfo MFindType; private static bool _ready; private static Type TSemi; private static MethodInfo MIsHost; private static bool _host; private static float _hostNext; private static bool _hostLogged; private static readonly Dictionary<int, FxState> _fx = new Dictionary<int, FxState>(); private static readonly List<int> _fxGone = new List<int>(); private static int _fxOn; private static bool _fxWarned; private static readonly Dictionary<int, float> _protUntil = new Dictionary<int, float>(); private static readonly List<int> _protGone = new List<int>(); private static Type TImp; private static bool _gWarned; public static bool IsHost() { if (Time.unscaledTime < _hostNext) { return _host; } _hostNext = Time.unscaledTime + 1f; try { if (TSemi == null) { TSemi = R.FindType("SemiFunc"); } if (MIsHost == null && TSemi != null) { MIsHost = TSemi.GetMethod("IsMasterClientOrSingleplayer", BindingFlags.Static | BindingFlags.Public, null, Type.EmptyTypes, null); } if (MIsHost == null) { _host = true; return true; } _host = (bool)MIsHost.Invoke(null, null); } catch { _host = true; } return _host; } public static void SetGuard(Component pgo, Tracked tr, bool on) { if ((Object)(object)pgo == (Object)null || tr == null) { return; } try { GameObject gameObject = pgo.gameObject; if ((Object)(object)gameObject == (Object)null) { return; } if (TImp == null) { TImp = R.FindType("PhysGrabObjectImpactDetector"); } object obj = null; try { obj = R.Get(pgo, "impactDetector"); } catch { } if (obj == null && TImp != null) { obj = gameObject.GetComponent(TImp); } if (obj == null) { return; } if (on) { if (!tr.Guarded) { tr.Guarded = true; tr.OrigDisable = R.Bool(R.Get(obj, "destroyDisable")); R.Set(obj, "destroyDisable", true); Plugin.Log.LogInfo((object)("[guard] " + tr.Label + " ON was=" + tr.OrigDisable)); } } else if (tr.Guarded) { tr.Guarded = false; R.Set(obj, "destroyDisable", tr.OrigDisable); Plugin.Log.LogInfo((object)("[guard] " + tr.Label + " off")); } } catch (Exception ex) { if (!_gWarned) { _gWarned = true; Plugin.Log.LogWarning((object)("[guard] FAIL " + ex.GetType().Name + " " + ex.Message)); } } } public static void Protect(Component pgo, float seconds) { if ((Object)(object)pgo == (Object)null || seconds <= 0f) { return; } try { GameObject gameObject = pgo.gameObject; if (!((Object)(object)gameObject == (Object)null)) { int instanceID = ((Object)gameObject).GetInstanceID(); float num = Time.time + seconds; if (!_protUntil.TryGetValue(instanceID, out var value) || !(value > num - 0.05f)) { _protUntil[instanceID] = num; R.Call(pgo, "OverrideIndestructible", seconds); } } } catch { } } public static void ProtectTick() { if (_protUntil.Count == 0) { return; } _protGone.Clear(); foreach (KeyValuePair<int, float> item in _protUntil) { if (Time.time >= item.Value) { _protGone.Add(item.Key); } } for (int i = 0; i < _protGone.Count; i++) { _protUntil.Remove(_protGone[i]); } } private static void Ready() { if (!_ready) { TPgo = R.FindType("PhysGrabObject"); TVal = R.FindType("ValuableObject"); TEnemyVal = R.FindType("EnemyValuable"); try { MFindType = typeof(Object).GetMethod("FindObjectsOfType", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(Type) }, null); } catch { MFindType = null; } _ready = TPgo != null && TVal != null; } } private static Object[] FindAll(Type t) { try { if (MFindType != null && MFindType.Invoke(null, new object[1] { t }) is Object[] result) { return result; } } catch { } try { return Resources.FindObjectsOfTypeAll(t); } catch { } return (Object[])(object)new Object[0]; } public static void Tick() { //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_0211: 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_0236: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_0374: Unknown result type (might be due to invalid IL or missing references) //IL_035f: Unknown result type (might be due to invalid IL or missing references) //IL_03b6: Unknown result type (might be due to invalid IL or missing references) //IL_03bb: Unknown result type (might be due to invalid IL or missing references) //IL_03bd: Unknown result type (might be due to invalid IL or missing references) //IL_03c4: Unknown result type (might be due to invalid IL or missing references) //IL_03cc: Unknown result type (might be due to invalid IL or missing references) //IL_03d3: Unknown result type (might be due to invalid IL or missing references) if (Cfg.Enabled == null || !Cfg.Enabled.Value) { return; } if (!IsHost()) { if (!_hostLogged) { _hostLogged = true; Plugin.Log.LogWarning((object)"[host] NOT host, idle"); } return; } if (!_hostLogged) { _hostLogged = true; Plugin.Log.LogInfo((object)("[host] active isHost=true enabled=" + Cfg.Enabled.Value)); } ProtectTick(); Ready(); if (!_ready) { return; } FxTick(Time.time); float num = Mathf.Clamp(Cfg.ScanInterval.Value, 0.05f, 1f); if (Time.unscaledTime < _nextScan) { return; } _nextScan = Time.unscaledTime + num; Object[] array = FindAll(TPgo); if (array == null || array.Length == 0) { return; } float time = Time.time; float num2 = Mathf.Max(1.5f, Cfg.FallDepth.Value); float num3 = Mathf.Max(Cfg.PitY.Value, -30f); foreach (Object obj in array) { Component val = (Component)(object)((obj is Component) ? obj : null); if ((Object)(object)val == (Object)null) { continue; } GameObject gameObject = val.gameObject; if ((Object)(object)gameObject == (Object)null || !gameObject.activeInHierarchy) { continue; } bool flag = (Object)(object)gameObject.GetComponent(TVal) != (Object)null; if (!flag && Cfg.IncludeEnemyValuable.Value && TEnemyVal != null) { flag = (Object)(object)gameObject.GetComponent(TEnemyVal) != (Object)null; } if (!flag) { continue; } int instanceID = ((Object)gameObject).GetInstanceID(); if (!_map.TryGetValue(instanceID, out var value)) { value = new Tracked(); value.Label = LabelOf(val); _map[instanceID] = value; } value.Pgo = val; Vector3 position = val.transform.position; if (position.y < value.MinY) { value.MinY = position.y; } if (position.y > Cfg.SafeMinY.Value && position.y < Cfg.SafeMaxY.Value && time - value.SafeAt > 0.4f) { value.SafePos = position; value.SafeAt = time; value.HasSafe = true; } float num4 = 0f; object obj2 = R.Get(val, "rb"); Rigidbody val2 = (Rigidbody)((obj2 is Rigidbody) ? obj2 : null); if ((Object)(object)val2 != (Object)null) { num4 = val2.velocity.y; } bool flag2 = GrabCount(val) > 0; if (flag2 && !value.WasHeld) { value.LastGrab = time; } value.WasHeld = flag2; bool num5 = value.HasSafe && position.y < value.SafePos.y - num2; bool flag3 = position.y < num3; if ((num5 || flag3) && !flag2) { SetGuard(val, value, on: true); value.LastPit = time; value.CalmAt = 0f; if (time >= value.NextPush && num4 < 1f) { value.NextPush = time + Mathf.Clamp(Cfg.PushInterval.Value, 0.6f, 1.5f); value.LastSave = time; value.Saves++; Bounce(val, value, position); } continue; } bool flag4 = !value.HasSafe || position.y >= value.SafePos.y - 1f; if (flag4) { value.Saves = 0; } float num6 = 0f; if ((Object)(object)val2 != (Object)null) { Vector3 velocity = val2.velocity; num6 = Mathf.Sqrt(velocity.x * velocity.x + velocity.z * velocity.z); } bool flag5 = !flag2 && flag4 && Mathf.Abs(num4) < 0.6f && num6 < 1.2f; if (flag5) { if (value.CalmAt <= 0f) { value.CalmAt = time; } } else { value.CalmAt = 0f; } if (flag2) { SetGuard(val, value, on: false); } else if (flag5 && time - value.CalmAt > 1.2f) { SetGuard(val, value, on: false); } if (flag2 || flag4) { FxOff(instanceID); } } Cleanup(time); } private static int GrabCount(Component pgo) { object obj = R.Get(pgo, "playerGrabbing"); if (obj == null) { return 0; } if (obj is IList list) { return list.Count; } try { PropertyInfo property = obj.GetType().GetProperty("Count"); if (property != null) { return R.Int(property.GetValue(obj)); } } catch { } return 0; } public static void Bounce(Component pgo, Tracked tr, Vector3 cur) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: 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_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) try { Vector3 val = ((tr != null && tr.HasSafe) ? tr.SafePos : FallbackPos()); Vector3 val2 = val - cur; val2.y = 0f; float magnitude = ((Vector3)(ref val2)).magnitude; if (magnitude < 0.01f) { val2 = Vector3.forward; } val2 = ((Vector3)(ref val2)).normalized; if (Cfg.NativePitBounce != null && Cfg.NativePitBounce.Value) { try { R.Call(pgo, "DeathPitEffectCreate"); } catch { } } object obj2 = R.Get(pgo, "rb"); Rigidbody val3 = (Rigidbody)((obj2 is Rigidbody) ? obj2 : null); if ((Object)(object)val3 == (Object)null) { Vector3 val4 = val; val4.y += Mathf.Max(0.5f, Cfg.LiftY.Value); try { R.Call(pgo, "Teleport", val4, pgo.transform.rotation); } catch { } pgo.transform.position = val4; Plugin.Log.LogInfo((object)("[tp] " + LabelOf(pgo) + " y=" + cur.y.ToString("F1") + " (no rigidbody)")); return; } float num = Mathf.Clamp(Cfg.BounceSpeed.Value, 2f, 60f); float num2 = Mathf.Max(0f, val.y - cur.y); float num3 = Mathf.Clamp(Cfg.PushRise.Value, 0.3f, 3f); float num4 = (6f + Mathf.Min(num2, 10f) * 0.25f) * num3; num4 = Mathf.Clamp(num4, 4f, 26f); Vector3 velocity = val2 * num; velocity.y = num4; if (val3.isKinematic) { val3.isKinematic = false; } val3.velocity = velocity; val3.angularVelocity = Vector3.zero; if (Cfg.ShowToast.Value) { Toast.Push(LabelOf(pgo)); } Plugin.Log.LogInfo((object)("[bounce] " + LabelOf(pgo) + " y=" + cur.y.ToString("F1") + " need=" + num2.ToString("F1") + " up=" + num4.ToString("F1") + " flat=" + magnitude.ToString("F1") + " saves=" + (tr?.Saves ?? 0))); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[bounce] FAIL " + ex.GetType().Name + " " + ex.Message)); } } private static string LabelOf(Component pgo) { try { Component component = pgo.gameObject.GetComponent(TVal); if ((Object)(object)component != (Object)null) { float num = R.Num(R.Get(component, "dollarValueCurrent")); if (num > 0.5f) { return "$" + Mathf.RoundToInt(num); } } return ((Object)pgo.gameObject).name; } catch { } return "item"; } private static Vector3 FallbackPos() { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) try { Type type = R.FindType("TruckSafetySpawnPoint"); if (type != null) { object obj = R.Static(type, "instance"); Component val = (Component)((obj is Component) ? obj : null); if ((Object)(object)val != (Object)null) { return val.transform.position; } } } catch { } try { Type type2 = R.FindType("PlayerController"); if (type2 != null) { object obj3 = R.Static(type2, "instance"); Component val2 = (Component)((obj3 is Component) ? obj3 : null); if ((Object)(object)val2 != (Object)null) { return val2.transform.position + Vector3.up * 2f; } } } catch { } return new Vector3(0f, 2f, 0f); } private static FxState FxGet(Component pgo, int id) { //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) try { if (_fx.TryGetValue(id, out var value) && value != null && (Object)(object)value.Go != (Object)null) { return value; } object obj = R.Get(pgo, "deathPitEffect"); GameObject val = (GameObject)((obj is GameObject) ? obj : null); if ((Object)(object)val == (Object)null) { R.Call(pgo, "DeathPitEffectCreate"); object obj2 = R.Get(pgo, "deathPitEffect"); val = (GameObject)((obj2 is GameObject) ? obj2 : null); } if ((Object)(object)val == (Object)null) { if (!_fxWarned) { _fxWarned = true; Plugin.Log.LogWarning((object)"[fx] 没建出特效"); } return null; } if (TEffect == null) { TEffect = R.FindType("DeathPitSaveEffect"); } value = new FxState(); value.Go = val; if (TEffect != null) { value.Eff = val.GetComponent(TEffect); } try { value.Ps = val.GetComponentsInChildren<ParticleSystem>(true); } catch { value.Ps = null; } try { val.transform.localPosition = Vector3.zero; val.transform.localRotation = Quaternion.identity; } catch { } FxMute(value); _fx[id] = value; return value; } catch (Exception ex) { if (!_fxWarned) { _fxWarned = true; Plugin.Log.LogWarning((object)("[fx] get-fail " + ex.GetType().Name + " " + ex.Message)); } return null; } } private static void FxMute(FxState st) { if (st == null) { return; } try { if ((Object)(object)st.Eff != (Object)null) { R.Set(st.Eff, "timeCurrent", 0f); } } catch { } try { if (st.Ps == null) { return; } for (int i = 0; i < st.Ps.Length; i++) { ParticleSystem val = st.Ps[i]; if ((Object)(object)val != (Object)null) { val.Stop(true, (ParticleSystemStopBehavior)0); } } } catch { } } private static void LightFx(Component pgo, int id) { try { if (Cfg.NativePitBounce != null && !Cfg.NativePitBounce.Value) { return; } FxState fxState = FxGet(pgo, id); if (fxState == null) { return; } fxState.Until = Time.time + 15f; fxState.NextReset = 0f; try { if ((Object)(object)fxState.Eff != (Object)null) { R.Call(fxState.Eff, "Reset"); } } catch { } try { if (fxState.Ps != null) { for (int i = 0; i < fxState.Ps.Length; i++) { ParticleSystem val = fxState.Ps[i]; if ((Object)(object)val != (Object)null && !val.isPlaying) { val.Play(); } } } } catch { } _fxOn++; if (_fxOn <= 3) { Plugin.Log.LogInfo((object)("[fx] on #" + _fxOn + " " + ((Object)pgo.gameObject).name)); } } catch (Exception ex) { if (!_fxWarned) { _fxWarned = true; Plugin.Log.LogWarning((object)("[fx] on-fail " + ex.GetType().Name + " " + ex.Message)); } } } private static void FxOff(int id) { if (_fx.TryGetValue(id, out var value)) { FxMute(value); _fx.Remove(id); if (_fxOn <= 3) { Plugin.Log.LogInfo((object)"[fx] off"); } } } public static void FxTick(float now) { if (_fx.Count == 0) { return; } _fxGone.Clear(); foreach (KeyValuePair<int, FxState> item in _fx) { FxState value = item.Value; if (value == null || (Object)(object)value.Go == (Object)null || now > value.Until) { if (value != null) { FxMute(value); } _fxGone.Add(item.Key); continue; } try { if ((Object)(object)value.Eff != (Object)null) { R.Set(value.Eff, "timeCurrent", 2.5f); if (now >= value.NextReset) { value.NextReset = now + 1f; R.Call(value.Eff, "Reset"); } } } catch { } try { if (value.Ps == null) { continue; } for (int i = 0; i < value.Ps.Length; i++) { ParticleSystem val = value.Ps[i]; if ((Object)(object)val != (Object)null && !val.isPlaying) { val.Play(); } } } catch { } } for (int j = 0; j < _fxGone.Count; j++) { _fx.Remove(_fxGone[j]); } } private static void Cleanup(float now) { if (now < _nextClean) { return; } _nextClean = now + 10f; if (_map.Count == 0) { return; } _gone.Clear(); foreach (KeyValuePair<int, Tracked> item in _map) { Tracked value = item.Value; if (value == null || (Object)(object)value.Pgo == (Object)null) { if (value != null) { if (value.Guarded) { try { SetGuard(value.Pgo, value, on: false); } catch { } } Plugin.Log.LogInfo((object)("[gone] " + value.Label + " minY=" + value.MinY.ToString("F1") + " saves=" + value.Saves)); } _gone.Add(item.Key); } else if ((Object)(object)value.Pgo.gameObject == (Object)null) { _gone.Add(item.Key); } } for (int i = 0; i < _gone.Count; i++) { _map.Remove(_gone[i]); } } } [BepInPlugin("com.yanyan.pitsave", "PitSave", "1.1.0")] public class Plugin : BaseUnityPlugin { public const string GUID = "com.yanyan.pitsave"; public const string NAME = "PitSave"; public const string VERSION = "1.1.0"; internal static ManualLogSource Log; internal static Plugin Instance; private float _hbNext; private float _errNext; private int _tickCount; private void Awake() { //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; try { Cfg.Bind(((BaseUnityPlugin)this).Config); } catch (Exception ex) { Log.LogError((object)("[awake] cfg ex " + ex.Message)); } try { Lang.Hook(); } catch (Exception ex2) { Log.LogError((object)("[awake] lang hook ex " + ex2.Message)); } GameObject gameObject = ((Component)this).gameObject; PitRunner pitRunner = null; try { pitRunner = gameObject.AddComponent<PitRunner>(); } catch (Exception ex3) { Log.LogError((object)("[awake] add runner ex " + ex3.Message)); } ManualLogSource log = Log; string[] obj = new string[10] { "PitSave 1.1.0 loaded runner=", ((Object)(object)pitRunner != (Object)null).ToString(), " goAct=", gameObject.activeInHierarchy.ToString(), " hide=", ((object)((Object)gameObject).hideFlags/*cast due to .constrained prefix*/).ToString(), " selfEn=", ((Behaviour)this).enabled.ToString(), " scene=", null }; Scene activeScene = SceneManager.GetActiveScene(); obj[9] = ((Scene)(ref activeScene)).name; log.LogInfo((object)string.Concat(obj)); } private void Start() { Log.LogInfo((object)"[start] plugin Start fired"); } private void Update() { //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) _tickCount++; try { Lang.RefreshTick(); } catch { } try { Pit.Tick(); } catch (Exception ex) { if (Time.unscaledTime > _errNext) { _errNext = Time.unscaledTime + 5f; if (Log != null) { Log.LogError((object)("[tick-ex] " + ex.GetType().Name + " " + ex.Message)); } } } if (Time.unscaledTime > _hbNext) { _hbNext = Time.unscaledTime + 15f; bool flag = Cfg.Enabled != null && Cfg.Enabled.Value; bool flag2 = false; try { flag2 = Pit.IsHost(); } catch { } ManualLogSource log = Log; string[] obj3 = new string[8] { "[hb] alive en=", flag.ToString(), " host=", flag2.ToString(), " ticks=", _tickCount.ToString(), " lvl=", null }; Scene activeScene = SceneManager.GetActiveScene(); obj3[7] = ((Scene)(ref activeScene)).name; log.LogInfo((object)string.Concat(obj3)); } } private void OnGUI() { try { Toast.Draw(); } catch { } } } public class PitRunner : MonoBehaviour { private bool _said; private void Update() { if (!_said) { _said = true; if (Plugin.Log != null) { Plugin.Log.LogInfo((object)"[runner] own Update fired"); } } } } internal static class R { private const BindingFlags BF = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private static readonly Dictionary<string, Type> _types = new Dictionary<string, Type>(); public static Type FindType(string simpleName) { if (string.IsNullOrEmpty(simpleName)) { return null; } if (_types.TryGetValue(simpleName, out var value)) { return value; } Type type = null; try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { Type[] types; try { types = assembly.GetTypes(); } catch { continue; } Type[] array = types; foreach (Type type2 in array) { if (type2.Name == simpleName) { type = type2; break; } } if (type != null) { break; } } } catch { } _types[simpleName] = type; return type; } public static object Get(object o, string name) { if (o == null) { return null; } try { Type type = o.GetType(); FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(field.IsStatic ? null : o); } PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanRead) { MethodInfo getMethod = property.GetGetMethod(nonPublic: true); if (getMethod != null) { return property.GetValue(getMethod.IsStatic ? null : o); } } } catch { } return null; } public static void Set(object o, string name, object v) { if (o == null) { return; } try { Type type = o.GetType(); FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(field.IsStatic ? null : o, v); return; } PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanWrite) { MethodInfo setMethod = property.GetSetMethod(nonPublic: true); if (setMethod != null) { property.SetValue(setMethod.IsStatic ? null : o, v); } } } catch { } } public static object Static(Type t, string name) { if (t == null) { return null; } try { FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.Name == name && fieldInfo.IsStatic) { return fieldInfo.GetValue(null); } } PropertyInfo[] properties = t.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo propertyInfo in properties) { MethodInfo getMethod = propertyInfo.GetGetMethod(nonPublic: true); if (propertyInfo.Name == name && getMethod != null && getMethod.IsStatic) { return propertyInfo.GetValue(null); } } } catch { } return null; } public static object Call(object o, string name, params object[] args) { if (o == null) { return null; } try { MethodInfo[] methods = o.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo.Name != name) && methodInfo.GetParameters().Length == args.Length) { try { return methodInfo.Invoke(methodInfo.IsStatic ? null : o, args); } catch { } } } } catch { } return null; } public static int Int(object o) { try { if (o == null) { return 0; } if (o is int result) { return result; } return Convert.ToInt32(o); } catch { return 0; } } public static float Num(object o) { try { if (o == null) { return 0f; } if (o is float result) { return result; } return Convert.ToSingle(o); } catch { return 0f; } } public static string Str(object o) { return o as string; } public static bool Bool(object o) { bool flag = default(bool); int num; if (o is bool) { flag = (bool)o; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } } internal static class Toast { private class Item { public string Text; public float Until; } private static readonly List<Item> _items = new List<Item>(); private static GUIStyle _st; public static void Push(string name) { _items.Add(new Item { Text = name, Until = Time.unscaledTime + 2.6f }); if (_items.Count > 5) { _items.RemoveAt(0); } } public static void Draw() { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) float unscaledTime = Time.unscaledTime; for (int num = _items.Count - 1; num >= 0; num--) { if (_items[num].Until < unscaledTime) { _items.RemoveAt(num); } } if (_items.Count != 0) { if (_st == null) { _st = new GUIStyle(GUI.skin.label); _st.fontSize = 17; _st.alignment = (TextAnchor)5; } float num2 = (float)Screen.height * 0.64f; for (int i = 0; i < _items.Count; i++) { Item item = _items[i]; float num3 = item.Until - unscaledTime; float num4 = ((num3 > 0.6f) ? 0.95f : (Mathf.Clamp01(num3 / 0.6f) * 0.95f)); _st.normal.textColor = new Color(0.55f, 1f, 0.62f, num4); GUI.Label(new Rect((float)Screen.width - 340f, num2 + (float)i * 23f, 320f, 23f), "PitSave saved " + item.Text, _st); } } } } }