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 Repo Optimization v1.0.1
RepoOptimization.dll
Decompiled 3 days agousing System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace RepoOptimization; [BepInPlugin("RepoOptimization", "Repo Optimization", "1.0.1")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "RepoOptimization"; public const string PluginName = "Repo Optimization"; public const string PluginVersion = "1.0.1"; internal static ManualLogSource Log; internal static ConfigEntry<bool> ModEnabled; internal static ConfigEntry<string> TargetNames; internal static ConfigEntry<int> LowestLevel; private static string[] _targets = new string[0]; [ThreadStatic] private static bool _busy; private Harmony _harmony; private void Awake() { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turn the reversed upgrades on or off."); TargetNames = ((BaseUnityPlugin)this).Config.Bind<string>("General", "TargetPlayerNames", "SofieLoafy", "Comma-separated player names whose upgrades are reversed (not case-sensitive)."); LowestLevel = ((BaseUnityPlugin)this).Config.Bind<int>("General", "LowestUpgradeLevel", -3, "How far below zero an upgrade level can go. Lower = harsher. Very low values (e.g. -5 health or stamina) can leave a stat at zero."); RefreshTargets(); TargetNames.SettingChanged += delegate { RefreshTargets(); }; _harmony = new Harmony("RepoOptimization"); PatchUpgradeMethods(); Pranks.Init(((BaseUnityPlugin)this).Config, _harmony); } private static void RefreshTargets() { _targets = (from n in (TargetNames.Value ?? string.Empty).Split(',') select n.Trim() into n where n.Length > 0 select n).ToArray(); } private void PatchUpgradeMethods() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown Type type = AccessTools.TypeByName("PunManager"); if (type == null) { Log.LogError((object)"Couldn't find PunManager. The game may have updated; nothing was patched."); return; } HarmonyMethod val = new HarmonyMethod(typeof(Plugin).GetMethod("UpgradePrefix", BindingFlags.Static | BindingFlags.NonPublic)); int num = 0; foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type)) { if (!declaredMethod.Name.StartsWith("Upgrade") || declaredMethod.Name.EndsWith("RPC")) { continue; } ParameterInfo[] parameters = declaredMethod.GetParameters(); if (parameters.Length == 2 && !(parameters[0].ParameterType != typeof(string)) && !(parameters[1].ParameterType != typeof(int)) && !(declaredMethod.ReturnType != typeof(int))) { try { _harmony.Patch((MethodBase)declaredMethod, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Reversing: PunManager." + declaredMethod.Name)); num++; } catch (Exception ex) { Log.LogWarning((object)("Couldn't patch " + declaredMethod.Name + ": " + ex.Message)); } } } if (num == 0) { Log.LogWarning((object)"No upgrade methods matched. The game may have changed how upgrades work."); } else { Log.LogInfo((object)("Repo Optimization loaded. Patched " + num + " upgrade types.")); } } private static bool UpgradePrefix(object __instance, MethodBase __originalMethod, object[] __args, ref int __result) { if (_busy || !ModEnabled.Value || __args == null || __args.Length < 2) { return true; } string text = __args[0] as string; if (!(__args[1] is int)) { return true; } int num = (int)__args[1]; if (num <= 0 || !IsTargetPlayer(text)) { return true; } _busy = true; try { int num2 = (int)__originalMethod.Invoke(__instance, new object[2] { text, -num }); int value = LowestLevel.Value; if (num2 < value) { num2 = (int)__originalMethod.Invoke(__instance, new object[2] { text, value - num2 }); } __result = num2; Log.LogInfo((object)(__originalMethod.Name + " reversed for " + text + " -> level " + num2)); return false; } catch (Exception ex) { Log.LogError((object)("Reverse failed, letting the normal upgrade through: " + ex)); return true; } finally { _busy = false; } } private static bool IsTargetPlayer(string steamId) { if (string.IsNullOrEmpty(steamId) || _targets.Length == 0) { return false; } return NameIsTarget(GetPlayerName(steamId)); } internal static bool NameIsTarget(string name) { if (string.IsNullOrEmpty(name) || _targets.Length == 0) { return false; } name = name.Trim(); return _targets.Any((string t) => string.Equals(t, name, StringComparison.OrdinalIgnoreCase)); } private static string GetPlayerName(string steamId) { Type type = AccessTools.TypeByName("SemiFunc"); if (type == null) { return null; } object obj = null; MethodInfo methodInfo = AccessTools.Method(type, "PlayerAvatarGetFromSteamID", new Type[1] { typeof(string) }, (Type[])null); if (methodInfo != null) { obj = methodInfo.Invoke(null, new object[1] { steamId }); } if (IsMissing(obj)) { obj = null; if (AccessTools.Method(type, "PlayerGetAll", (Type[])null, (Type[])null)?.Invoke(null, null) is IEnumerable enumerable) { foreach (object item in enumerable) { if (!IsMissing(item) && GetSteamId(type, item) == steamId) { obj = item; break; } } } } return AvatarName(obj); } internal static string AvatarName(object avatar) { if (IsMissing(avatar)) { return null; } Type type = AccessTools.TypeByName("SemiFunc"); string text = Traverse.Create(avatar).Field("playerName").GetValue() as string; if (string.IsNullOrEmpty(text) && type != null) { MethodInfo methodInfo = AccessTools.Method(type, "PlayerGetName", (Type[])null, (Type[])null); if (methodInfo != null) { text = methodInfo.Invoke(null, new object[1] { avatar }) as string; } } return text; } private static string GetSteamId(Type semiFunc, object avatar) { MethodInfo methodInfo = AccessTools.Method(semiFunc, "PlayerGetSteamID", (Type[])null, (Type[])null); if (methodInfo != null) { return methodInfo.Invoke(null, new object[1] { avatar }) as string; } return Traverse.Create(avatar).Field("steamID").GetValue() as string; } internal static bool IsMissing(object obj) { if (obj == null) { return true; } Object val = (Object)((obj is Object) ? obj : null); return val != (Object)null && val == (Object)null; } } internal class Pranks : MonoBehaviour { private static ConfigEntry<bool> _butterOn; private static ConfigEntry<bool> _drunkOn; private static ConfigEntry<bool> _tumbleOn; private static ConfigEntry<bool> _staminaOn; private static ConfigEntry<bool> _ghostOn; private static ConfigEntry<bool> _mapOn; private static ConfigEntry<bool> _mapDecoys; private static ConfigEntry<float> _butterAvg; private static ConfigEntry<float> _drunkAvg; private static ConfigEntry<float> _drunkLength; private static ConfigEntry<float> _driftStrength; private static ConfigEntry<float> _tumbleAvg; private static ConfigEntry<float> _drainMult; private static ConfigEntry<float> _regenMult; private static ConfigEntry<float> _ghostAvg; private static ConfigEntry<float> _ghostVolume; private static ConfigEntry<float> _mapHideChance; internal static bool ControlsSwapped; private static Type _semiFunc; private static Type _playerAvatar; private static Type _playerController; private static Type _physGrabber; private static Type _mapValuable; private object _avatar; private bool _isTarget; private bool _inLevel; private float _checkTimer; private float _drunkTimer; private float _tumbleEndTimer; private float _mapTimer; private float _ghostScanTimer; private float _lastEnergy = -1f; private object _tumblingObject; private List<AudioClip> _ghostClips = new List<AudioClip>(); private readonly Dictionary<int, bool> _mapDecided = new Dictionary<int, bool>(); private readonly List<GameObject> _decoys = new List<GameObject>(); private static readonly string[] GhostWords = new string[13] { "footstep", "step", "growl", "scream", "breath", "whisper", "monster", "enemy", "creak", "knock", "door", "groan", "laugh" }; private readonly HashSet<string> _broken = new HashSet<string>(); public static void Init(ConfigFile cfg, Harmony harmony) { //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Expected O, but got Unknown _butterOn = cfg.Bind<bool>("1. Butterfingers", "Enabled", true, "Randomly drop whatever is being held."); _butterAvg = cfg.Bind<float>("1. Butterfingers", "AverageSecondsBetweenDrops", 45f, "Average seconds of holding something before it slips."); _drunkOn = cfg.Bind<bool>("4. Drunk Walk", "Enabled", true, "Left/right controls swap now and then, with a sideways drift."); _drunkAvg = cfg.Bind<float>("4. Drunk Walk", "AverageSecondsBetweenSpells", 120f, "Average seconds between drunk spells."); _drunkLength = cfg.Bind<float>("4. Drunk Walk", "SpellLengthSeconds", 8f, "How long each drunk spell lasts."); _driftStrength = cfg.Bind<float>("4. Drunk Walk", "DriftStrength", 4f, "How hard the sideways drift pushes. 0 = no drift."); _tumbleOn = cfg.Bind<bool>("5. Random Tumbles", "Enabled", true, "Trip into a tumble for no reason."); _tumbleAvg = cfg.Bind<float>("5. Random Tumbles", "AverageSecondsBetweenTrips", 180f, "Average seconds between trips."); _staminaOn = cfg.Bind<bool>("6. Shrinking Stamina", "Enabled", true, "Stamina drains faster and refills slower."); _drainMult = cfg.Bind<float>("6. Shrinking Stamina", "DrainMultiplier", 1.4f, "1.4 = stamina drains 40% faster."); _regenMult = cfg.Bind<float>("6. Shrinking Stamina", "RegenMultiplier", 0.6f, "0.6 = stamina refills at 60% speed."); _ghostOn = cfg.Bind<bool>("11. Ghost Sounds", "Enabled", true, "Play creepy game sounds behind the player. Only they hear them."); _ghostAvg = cfg.Bind<float>("11. Ghost Sounds", "AverageSecondsBetweenSounds", 75f, "Average seconds between ghost sounds."); _ghostVolume = cfg.Bind<float>("11. Ghost Sounds", "Volume", 0.8f, "0 to 1."); _mapOn = cfg.Bind<bool>("13. Map Lies", "Enabled", true, "Hide some valuables from the map."); _mapHideChance = cfg.Bind<float>("13. Map Lies", "HideChance", 0.4f, "Chance (0 to 1) that each valuable is hidden from the map."); _mapDecoys = cfg.Bind<bool>("13. Map Lies", "FakeMarkers", true, "Put a fake marker in the wrong spot for each hidden one."); _semiFunc = AccessTools.TypeByName("SemiFunc"); _playerAvatar = AccessTools.TypeByName("PlayerAvatar"); _playerController = AccessTools.TypeByName("PlayerController"); _physGrabber = AccessTools.TypeByName("PhysGrabber"); _mapValuable = AccessTools.TypeByName("MapValuable"); PatchMovementInput(harmony); GameObject val = new GameObject("RepoOptimization.Pranks"); ((Object)val).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)val); val.AddComponent<Pranks>(); } private void Update() { float dt = Time.deltaTime; _checkTimer -= dt; if (_checkTimer <= 0f) { _checkTimer = 1f; _avatar = GetLocalAvatar(); _isTarget = Plugin.ModEnabled.Value && Plugin.NameIsTarget(Plugin.AvatarName(_avatar)); _inLevel = IsInLevel(); } if (!_isTarget || !_inLevel) { ControlsSwapped = false; _lastEnergy = -1f; return; } Safe("Butterfingers", delegate { if (_butterOn.Value) { Butterfingers(); } }); Safe("Drunk walk", delegate { DrunkWalk(dt); }); Safe("Random tumbles", delegate { RandomTumbles(dt); }); Safe("Ghost sounds", delegate { if (_ghostOn.Value) { GhostSounds(dt); } }); Safe("Map lies", delegate { if (_mapOn.Value) { MapLies(dt); } }); } private void LateUpdate() { if (!_isTarget || !_inLevel) { return; } Safe("Shrinking stamina", delegate { if (_staminaOn.Value) { ShrinkingStamina(); } else { _lastEnergy = -1f; } }); } private void FixedUpdate() { if (!_isTarget || !_inLevel || !ControlsSwapped || _driftStrength.Value <= 0f) { return; } Safe("Drunk drift", delegate { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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) //IL_007a: Unknown result type (might be due to invalid IL or missing references) object field = GetField(StaticInstance(_playerController), "rb"); Rigidbody val = (Rigidbody)((field is Rigidbody) ? field : null); Camera main = Camera.main; if (!((Object)(object)val == (Object)null) && !((Object)(object)main == (Object)null)) { Vector3 right = ((Component)main).transform.right; right.y = 0f; float num = Mathf.Sin(Time.time * 1.3f); val.AddForce(((Vector3)(ref right)).normalized * num * _driftStrength.Value, (ForceMode)5); } }); } private void Butterfingers() { object obj = StaticInstance(_physGrabber); if (Plugin.IsMissing(obj)) { return; } object field = GetField(obj, "grabbed"); if (field is bool && (bool)field && Roll(_butterAvg.Value)) { MethodInfo methodInfo = AccessTools.GetDeclaredMethods(_physGrabber).FirstOrDefault((MethodInfo m) => m.Name == "ReleaseObject"); if (methodInfo != null) { methodInfo.Invoke(obj, DefaultArgs(methodInfo)); } } } private void DrunkWalk(float dt) { if (!_drunkOn.Value) { ControlsSwapped = false; } else if (ControlsSwapped) { _drunkTimer -= dt; if (_drunkTimer <= 0f) { ControlsSwapped = false; } } else if (Roll(_drunkAvg.Value)) { ControlsSwapped = true; _drunkTimer = _drunkLength.Value; } } private static void PatchMovementInput(Harmony harmony) { //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Expected O, but got Unknown Type type = AccessTools.TypeByName("InputManager"); List<MethodInfo> list = new List<MethodInfo>(); if (type != null) { list.Add(AccessTools.Method(type, "GetMovementX", (Type[])null, (Type[])null)); list.Add(AccessTools.Method(type, "GetMovement", (Type[])null, (Type[])null)); } if (_semiFunc != null) { list.Add(AccessTools.Method(_semiFunc, "InputMovementX", (Type[])null, (Type[])null)); } foreach (MethodInfo item in list) { if (item == null) { continue; } string text = ((item.ReturnType == typeof(float)) ? "FlipFloat" : ((!(item.ReturnType == typeof(Vector2))) ? null : "FlipVector2")); if (text != null) { try { MethodInfo methodInfo = item; HarmonyMethod val = new HarmonyMethod(typeof(Pranks).GetMethod(text, BindingFlags.Static | BindingFlags.NonPublic)); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)("Drunk walk hooked into " + item.DeclaringType.Name + "." + item.Name)); return; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Couldn't hook " + item.Name + ": " + ex.Message)); } } } Plugin.Log.LogWarning((object)"Drunk walk: no movement input found to swap, so only the drift will happen."); } private static void FlipFloat(ref float __result) { if (ControlsSwapped) { __result = 0f - __result; } } private static void FlipVector2(ref Vector2 __result) { if (ControlsSwapped) { __result.x = 0f - __result.x; } } private void RandomTumbles(float dt) { if (_tumblingObject != null) { _tumbleEndTimer -= dt; if (_tumbleEndTimer <= 0f) { InvokeTumble(_tumblingObject, start: false); _tumblingObject = null; } } else { if (!_tumbleOn.Value || !Roll(_tumbleAvg.Value)) { return; } object field = GetField(_avatar, "tumble"); if (!Plugin.IsMissing(field)) { object field2 = GetField(field, "isTumbling"); if ((!(field2 is bool) || !(bool)field2) && InvokeTumble(field, start: true)) { _tumblingObject = field; _tumbleEndTimer = Random.Range(1.5f, 3f); } } } } private static bool InvokeTumble(object tumble, bool start) { MethodInfo methodInfo = AccessTools.GetDeclaredMethods(tumble.GetType()).FirstOrDefault((MethodInfo m) => m.Name == "TumbleRequest"); if (methodInfo == null) { return false; } object[] array = DefaultArgs(methodInfo); if (array.Length > 0 && array[0] is bool) { array[0] = start; } if (array.Length > 1 && array[1] is bool) { array[1] = false; } methodInfo.Invoke(tumble, array); return true; } private void ShrinkingStamina() { object obj = StaticInstance(_playerController); FieldInfo fieldInfo = ((!(_playerController != null)) ? null : AccessTools.Field(_playerController, "EnergyCurrent")); if (Plugin.IsMissing(obj) || fieldInfo == null || fieldInfo.FieldType != typeof(float)) { return; } float num = (float)fieldInfo.GetValue(obj); if (_lastEnergy >= 0f) { float num2 = num - _lastEnergy; if (num2 > 0f && num2 < 20f) { num = _lastEnergy + num2 * _regenMult.Value; } else if (num2 < 0f) { num = Mathf.Max(0f, _lastEnergy + num2 * _drainMult.Value); } fieldInfo.SetValue(obj, num); } _lastEnergy = num; } private void GhostSounds(float dt) { //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) _ghostScanTimer -= dt; if (_ghostClips.Count == 0 && _ghostScanTimer <= 0f) { _ghostScanTimer = 30f; _ghostClips = (from c in Resources.FindObjectsOfTypeAll<AudioClip>() where (Object)(object)c != (Object)null && c.length > 0.2f && c.length < 8f && GhostWords.Any((string w) => ((Object)c).name.IndexOf(w, StringComparison.OrdinalIgnoreCase) >= 0) select c).ToList(); if (_ghostClips.Count > 0) { Plugin.Log.LogInfo((object)("Ghost sounds: found " + _ghostClips.Count + " creepy clips.")); } } if (_ghostClips.Count == 0 || !Roll(_ghostAvg.Value)) { return; } Camera main = Camera.main; if (!((Object)(object)main == (Object)null)) { AudioClip val = _ghostClips[Random.Range(0, _ghostClips.Count)]; if ((Object)(object)val == (Object)null) { _ghostClips.Clear(); return; } Transform transform = ((Component)main).transform; Vector3 val2 = transform.position - transform.forward * Random.Range(3f, 7f) + transform.right * Random.Range(-3f, 3f); AudioSource.PlayClipAtPoint(val, val2, Mathf.Clamp01(_ghostVolume.Value)); } } private void MapLies(float dt) { _mapTimer -= dt; if (_mapTimer > 0f || _mapValuable == null) { return; } _mapTimer = 0.3f; Object[] array = Object.FindObjectsOfType(_mapValuable); if (array.Length == 0) { _mapDecided.Clear(); _decoys.RemoveAll((GameObject d) => (Object)(object)d == (Object)null); return; } List<Component> list = (from c in array.OfType<Component>() where (Object)(object)c != (Object)null && (Object)(object)c.GetComponentInParent<Rigidbody>() == (Object)null select c).ToList(); foreach (Component item in list) { int instanceID = ((Object)item).GetInstanceID(); if (!_mapDecided.TryGetValue(instanceID, out var value)) { value = Random.value < _mapHideChance.Value; _mapDecided[instanceID] = value; if (value && _mapDecoys.Value && list.Count >= 2) { MakeDecoy(item, list); } } if (value) { Renderer[] componentsInChildren = item.GetComponentsInChildren<Renderer>(true); foreach (Renderer val in componentsInChildren) { val.enabled = false; } } } } private void MakeDecoy(Component marker, List<Component> all) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate<GameObject>(marker.gameObject, marker.transform.parent); MonoBehaviour[] componentsInChildren = val.GetComponentsInChildren<MonoBehaviour>(true); foreach (MonoBehaviour val2 in componentsInChildren) { Object.Destroy((Object)(object)val2); } Renderer[] componentsInChildren2 = val.GetComponentsInChildren<Renderer>(true); foreach (Renderer val3 in componentsInChildren2) { val3.enabled = true; } Vector3 position = all[Random.Range(0, all.Count)].transform.position; Vector3 position2 = all[Random.Range(0, all.Count)].transform.position; val.transform.position = Vector3.Lerp(position, position2, Random.Range(0.3f, 0.7f)); _decoys.Add(val); } private static bool Roll(float avgSeconds) { if (avgSeconds <= 0f) { return false; } return Random.value < Time.deltaTime / avgSeconds; } private static object GetLocalAvatar() { if (_semiFunc != null) { MethodInfo methodInfo = AccessTools.Method(_semiFunc, "PlayerAvatarLocal", (Type[])null, (Type[])null); if (methodInfo != null) { object obj = methodInfo.Invoke(null, null); if (!Plugin.IsMissing(obj)) { return obj; } } } object obj2 = StaticInstance(_playerAvatar); return (!Plugin.IsMissing(obj2)) ? obj2 : null; } private static bool IsInLevel() { if (_semiFunc == null) { return true; } MethodInfo methodInfo = AccessTools.Method(_semiFunc, "RunIsLevel", (Type[])null, (Type[])null); if (methodInfo == null) { return true; } object obj = methodInfo.Invoke(null, null); return !(obj is bool) || (bool)obj; } private static object StaticInstance(Type t) { if (t == null) { return null; } FieldInfo fieldInfo = AccessTools.Field(t, "instance") ?? AccessTools.Field(t, "Instance"); return (!(fieldInfo != null) || !fieldInfo.IsStatic) ? null : fieldInfo.GetValue(null); } private static object GetField(object obj, string name) { if (Plugin.IsMissing(obj)) { return null; } FieldInfo fieldInfo = AccessTools.Field(obj.GetType(), name); return (!(fieldInfo != null)) ? null : fieldInfo.GetValue(obj); } private static object[] DefaultArgs(MethodInfo m) { return (from p in m.GetParameters() select p.HasDefaultValue ? p.DefaultValue : ((!p.ParameterType.IsValueType) ? null : Activator.CreateInstance(p.ParameterType))).ToArray(); } private void Safe(string effect, Action action) { if (_broken.Contains(effect)) { return; } try { action(); } catch (Exception ex) { _broken.Add(effect); Plugin.Log.LogWarning((object)(effect + " stopped working and was turned off: " + ex.Message)); } } }