Decompiled source of Repo Gameplay Tweaks v1.0.4

RepoGameplayTweaks.dll

Decompiled 8 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace RepoGameplayTweaks;

[BepInPlugin("RepoGameplayTweaks", "Repo Gameplay Tweaks", "1.0.4")]
public class Plugin : BaseUnityPlugin
{
	public const string PluginGuid = "RepoGameplayTweaks";

	public const string PluginName = "Repo Gameplay Tweaks";

	public const string PluginVersion = "1.0.4";

	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];

	private static string[] _targetsNorm = new string[0];

	private static ConfigFile _config;

	internal static int PatchedCount;

	internal static volatile string PendingReversedName;

	private static DateTime _configWriteTime;

	private static readonly HashSet<string> _loggedChecks = new HashSet<string>();

	[ThreadStatic]
	private static bool _busy;

	private Harmony _harmony;

	private void Awake()
	{
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		_config = ((BaseUnityPlugin)this).Config;
		ModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turn the reversed upgrades on or off.");
		TargetNames = ((BaseUnityPlugin)this).Config.Bind<string>("General", "TargetPlayerNames", "Nyxeon", "Comma-separated player names (or Steam IDs) to prank. Capitals, spaces and symbols are ignored, so 'Nyxeon', 'nyxeon' and 'NYXEON' all match. Changes apply without restarting the game.");
		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();
		};
		try
		{
			_configWriteTime = File.GetLastWriteTimeUtc(((BaseUnityPlugin)this).Config.ConfigFilePath);
		}
		catch
		{
		}
		_harmony = new Harmony("RepoGameplayTweaks");
		PatchUpgradeMethods();
		Start("Pranks", StartPranks);
		Start("Startup check", StartIndicator);
		Start("Weapon hotkey", StartPerks);
	}

	private static void Start(string feature, Action start)
	{
		try
		{
			start();
		}
		catch (Exception ex)
		{
			Log.LogError((object)(feature + " failed to start: " + ex));
		}
	}

	[MethodImpl(MethodImplOptions.NoInlining)]
	private void StartPranks()
	{
		Pranks.Init(((BaseUnityPlugin)this).Config, _harmony);
	}

	[MethodImpl(MethodImplOptions.NoInlining)]
	private void StartIndicator()
	{
		Indicator.Init(((BaseUnityPlugin)this).Config);
	}

	[MethodImpl(MethodImplOptions.NoInlining)]
	private void StartPerks()
	{
		Perks.Init(((BaseUnityPlugin)this).Config);
	}

	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();
		_targetsNorm = _targets.Select(Norm).ToArray();
		_loggedChecks.Clear();
		Log.LogInfo((object)("Target players: " + ((_targets.Length != 0) ? string.Join(", ", _targets) : "(none)")));
	}

	internal static string TargetListText()
	{
		return (_targets.Length != 0) ? string.Join(", ", _targets) : "(none)";
	}

	internal static void PollConfigFile()
	{
		try
		{
			DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(_config.ConfigFilePath);
			if (!(lastWriteTimeUtc == _configWriteTime))
			{
				_configWriteTime = lastWriteTimeUtc;
				_config.Reload();
				RefreshTargets();
				Log.LogInfo((object)"Settings file changed, reloaded.");
			}
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("Couldn't reload settings: " + ex.Message));
		}
	}

	private static string Norm(string s)
	{
		if (s == null)
		{
			return string.Empty;
		}
		return new string(s.Where(char.IsLetterOrDigit).Select(char.ToLowerInvariant).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));
				}
			}
		}
		PatchedCount = num;
		if (num == 0)
		{
			Log.LogWarning((object)"No upgrade methods matched. The game may have changed how upgrades work.");
		}
		else
		{
			Log.LogInfo((object)("Repo Gameplay Tweaks 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));
			PendingReversedName = GetPlayerName(text) ?? "the target";
			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;
		}
		string playerName = GetPlayerName(steamId);
		bool flag = IdIsTarget(steamId) || NameIsTarget(playerName);
		string item = steamId + "|" + playerName + "|" + flag;
		if (_loggedChecks.Add(item))
		{
			Log.LogInfo((object)("Upgrade by '" + (playerName ?? "?") + "' (Steam ID " + steamId + "): " + ((!flag) ? "not a target" : "TARGET, reversing")));
		}
		return flag;
	}

	internal static bool IdIsTarget(string steamId)
	{
		return !string.IsNullOrEmpty(steamId) && _targets.Any((string t) => t == steamId.Trim());
	}

	internal static bool NameIsTarget(string name)
	{
		if (string.IsNullOrEmpty(name) || _targets.Length == 0)
		{
			return false;
		}
		string n = Norm(name);
		if (n.Length > 0 && _targetsNorm.Any((string t) => t.Length > 0 && t == n))
		{
			return true;
		}
		name = name.Trim();
		return _targets.Any((string t) => string.Equals(t, name, StringComparison.OrdinalIgnoreCase));
	}

	internal static bool AvatarIsTarget(object avatar)
	{
		if (IsMissing(avatar))
		{
			return false;
		}
		if (NameIsTarget(AvatarName(avatar)))
		{
			return true;
		}
		Type type = AccessTools.TypeByName("SemiFunc");
		return type != null && IdIsTarget(GetSteamId(type, avatar));
	}

	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;
	}

	internal 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 _configTimer;

	private bool _checkedOldMod;

	private string _lastStatus;

	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("RepoGameplayTweaks.Pranks");
		((Object)val).hideFlags = (HideFlags)61;
		Object.DontDestroyOnLoad((Object)(object)val);
		val.AddComponent<Pranks>();
	}

	private void Update()
	{
		float dt = Time.deltaTime;
		if (!_checkedOldMod)
		{
			_checkedOldMod = true;
			try
			{
				if (Chainloader.PluginInfos.ContainsKey("RepoOptimization"))
				{
					Plugin.Log.LogError((object)"The old 'Repo Optimization' mod is also installed! Uninstall it in r2modman, or the two mods cancel each other out.");
				}
			}
			catch
			{
			}
		}
		_configTimer -= dt;
		if (_configTimer <= 0f)
		{
			_configTimer = 2f;
			Plugin.PollConfigFile();
		}
		_checkTimer -= dt;
		if (_checkTimer <= 0f)
		{
			_checkTimer = 1f;
			_avatar = GetLocalAvatar();
			_isTarget = Plugin.ModEnabled.Value && Plugin.AvatarIsTarget(_avatar);
			_inLevel = IsInLevel();
			if (_avatar != null)
			{
				string text = "You are '" + Plugin.AvatarName(_avatar) + "': " + ((!_isTarget) ? "not a target, pranks are off" : "you ARE a target, pranks are ON");
				if (text != _lastStatus)
				{
					_lastStatus = text;
					Plugin.Log.LogInfo((object)text);
				}
			}
		}
		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;
	}

	internal 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;
	}

	internal 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));
		}
	}
}
internal class Perks : MonoBehaviour
{
	private static ConfigEntry<bool> _enabled;

	private static ConfigEntry<KeyboardShortcut> _weaponKey;

	private static ConfigEntry<string> _itemFilter;

	private static ConfigEntry<string> _specificItem;

	private static Type _semiFunc;

	private static Type _statsManager;

	private static Type _photonNetwork;

	private float _cooldown;

	public static void Init(ConfigFile cfg)
	{
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Expected O, but got Unknown
		_enabled = cfg.Bind<bool>("Perk: Spawn Weapon", "Enabled", true, "Press the hotkey to spawn a weapon in front of you. Only works as the host or in single player.");
		_weaponKey = cfg.Bind<KeyboardShortcut>("Perk: Spawn Weapon", "Hotkey", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Key to press.");
		_itemFilter = cfg.Bind<string>("Perk: Spawn Weapon", "ItemFilter", "gun,melee", "Comma-separated words. A random item whose name or type contains one of these is spawned.");
		_specificItem = cfg.Bind<string>("Perk: Spawn Weapon", "SpecificItem", string.Empty, "Leave blank for random. Or type an exact item name (e.g. Item Gun Shotgun) to always spawn that.");
		_semiFunc = AccessTools.TypeByName("SemiFunc");
		_statsManager = AccessTools.TypeByName("StatsManager");
		_photonNetwork = AccessTools.TypeByName("Photon.Pun.PhotonNetwork");
		GameObject val = new GameObject("RepoGameplayTweaks.Perks");
		((Object)val).hideFlags = (HideFlags)61;
		Object.DontDestroyOnLoad((Object)(object)val);
		val.AddComponent<Perks>();
	}

	private void Update()
	{
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		if (_cooldown > 0f)
		{
			_cooldown -= Time.deltaTime;
		}
		if (!Plugin.ModEnabled.Value || !_enabled.Value || _cooldown > 0f)
		{
			return;
		}
		KeyboardShortcut value = _weaponKey.Value;
		if (!((KeyboardShortcut)(ref value)).IsDown())
		{
			return;
		}
		_cooldown = 0.5f;
		try
		{
			SpawnWeapon();
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Spawn weapon failed: " + ex.Message));
		}
	}

	private static void SpawnWeapon()
	{
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: 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_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0270: Unknown result type (might be due to invalid IL or missing references)
		//IL_0271: Unknown result type (might be due to invalid IL or missing references)
		//IL_0203: Unknown result type (might be due to invalid IL or missing references)
		//IL_020c: Unknown result type (might be due to invalid IL or missing references)
		object obj = AccessTools.Method(_semiFunc, "PlayerAvatarLocal", (Type[])null, (Type[])null)?.Invoke(null, null);
		if (Plugin.IsMissing(obj) || Plugin.AvatarIsTarget(obj))
		{
			return;
		}
		if (!CallBool("IsMasterClientOrSingleplayer", fallback: true))
		{
			Plugin.Log.LogWarning((object)"Spawn weapon only works when you're the host (or in single player).");
			return;
		}
		object obj2 = PickItem();
		if (obj2 == null)
		{
			Plugin.Log.LogWarning((object)"Spawn weapon: no item matched ItemFilter / SpecificItem.");
			return;
		}
		Camera main = Camera.main;
		if ((Object)(object)main == (Object)null)
		{
			return;
		}
		Vector3 val = ((Component)main).transform.position + ((Component)main).transform.forward * 1.5f;
		Quaternion identity = Quaternion.identity;
		string name = ((Object)obj2).name;
		object value = Traverse.Create(obj2).Field("prefab").GetValue();
		GameObject val2 = (GameObject)((value is GameObject) ? value : null);
		string text;
		if ((Object)(object)val2 != (Object)null)
		{
			text = "Items/" + ((Object)val2).name;
		}
		else if (value != null)
		{
			Traverse val3 = Traverse.Create(value);
			text = (val3.Property("ResourcePath", (object[])null).GetValue() ?? val3.Field("resourcePath").GetValue()) as string;
			object value2 = val3.Property("Prefab", (object[])null).GetValue();
			val2 = (GameObject)((value2 is GameObject) ? value2 : null);
		}
		else
		{
			text = "Items/" + name;
		}
		if (CallBool("IsMultiplayer", fallback: false))
		{
			MethodInfo methodInfo = ((!(_photonNetwork == null)) ? AccessTools.GetDeclaredMethods(_photonNetwork).FirstOrDefault((MethodInfo m) => m.Name == "InstantiateRoomObject" && m.GetParameters().Length == 5) : null);
			if (methodInfo == null || string.IsNullOrEmpty(text))
			{
				Plugin.Log.LogWarning((object)"Spawn weapon: couldn't find how to spawn items online.");
				return;
			}
			methodInfo.Invoke(null, new object[5]
			{
				text,
				val,
				identity,
				(byte)0,
				null
			});
		}
		else
		{
			if ((Object)(object)val2 == (Object)null && !string.IsNullOrEmpty(text))
			{
				val2 = Resources.Load<GameObject>(text);
			}
			if ((Object)(object)val2 == (Object)null)
			{
				Plugin.Log.LogWarning((object)("Spawn weapon: couldn't load " + name));
				return;
			}
			Object.Instantiate<GameObject>(val2, val, identity);
		}
		Plugin.Log.LogInfo((object)("Spawned " + name));
	}

	private static object PickItem()
	{
		object obj = ((_statsManager == null) ? null : AccessTools.Field(_statsManager, "instance")?.GetValue(null));
		if (!(Traverse.Create(obj).Field("itemDictionary").GetValue() is IDictionary dictionary))
		{
			return null;
		}
		List<object> source = (from object i in dictionary.Values
			where !Plugin.IsMissing(i)
			select i).ToList();
		string specific = (_specificItem.Value ?? string.Empty).Trim();
		if (specific.Length > 0)
		{
			return source.FirstOrDefault((object i) => string.Equals(((Object)i).name, specific, StringComparison.OrdinalIgnoreCase) || string.Equals(Traverse.Create(i).Field("itemName").GetValue() as string, specific, StringComparison.OrdinalIgnoreCase));
		}
		string[] words = (from w in (_itemFilter.Value ?? string.Empty).Split(',')
			select w.Trim() into w
			where w.Length > 0
			select w).ToArray();
		List<object> list = source.Where(delegate(object i)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			string name = ((Object)i).name ?? string.Empty;
			string type = (Traverse.Create(i).Field("itemType").GetValue() ?? string.Empty).ToString();
			return words.Any((string w) => name.IndexOf(w, StringComparison.OrdinalIgnoreCase) >= 0 || type.IndexOf(w, StringComparison.OrdinalIgnoreCase) >= 0);
		}).ToList();
		return (list.Count != 0) ? list[Random.Range(0, list.Count)] : null;
	}

	private static bool CallBool(string method, bool fallback)
	{
		MethodInfo methodInfo = ((!(_semiFunc == null)) ? AccessTools.Method(_semiFunc, method, (Type[])null, (Type[])null) : null);
		if (methodInfo == null)
		{
			return fallback;
		}
		object obj = methodInfo.Invoke(null, null);
		return (!(obj is bool)) ? fallback : ((bool)obj);
	}
}
internal class Indicator : MonoBehaviour
{
	private static ConfigEntry<bool> _enabled;

	private static ConfigEntry<bool> _sound;

	private static ConfigEntry<bool> _text;

	private static ConfigEntry<float> _volume;

	private static Indicator _instance;

	private AudioSource _source;

	private AudioClip _chime;

	private AudioClip _bwomp;

	private bool _wasInLevel;

	private float _levelCheckTimer;

	private string _message;

	private float _messageUntil;

	private GUIStyle _style;

	private GUIStyle _shadowStyle;

	private const int Rate = 44100;

	public static void Init(ConfigFile cfg)
	{
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Expected O, but got Unknown
		_enabled = cfg.Bind<bool>("Startup Check", "Enabled", true, "Show that the mod is working. Never shown to target players.");
		_sound = cfg.Bind<bool>("Startup Check", "PlaySounds", true, "Chime when a level starts, and a 'bwomp' when a target's upgrade gets reversed.");
		_text = cfg.Bind<bool>("Startup Check", "ShowText", true, "Small message in the top-left corner for a few seconds.");
		_volume = cfg.Bind<float>("Startup Check", "Volume", 0.5f, "0 to 1.");
		GameObject val = new GameObject("RepoGameplayTweaks.Indicator");
		((Object)val).hideFlags = (HideFlags)61;
		Object.DontDestroyOnLoad((Object)(object)val);
		_instance = val.AddComponent<Indicator>();
	}

	private void Awake()
	{
		_source = ((Component)this).gameObject.AddComponent<AudioSource>();
		_source.spatialBlend = 0f;
		_chime = MakeChime();
		_bwomp = MakeBwomp();
	}

	private void Update()
	{
		if (Plugin.PendingReversedName != null)
		{
			string pendingReversedName = Plugin.PendingReversedName;
			Plugin.PendingReversedName = null;
			if (ShouldSignal())
			{
				Play(_bwomp);
				Show("Reversed " + pendingReversedName + "'s upgrade", 4f);
			}
		}
		_levelCheckTimer -= Time.deltaTime;
		if (!(_levelCheckTimer > 0f))
		{
			_levelCheckTimer = 1f;
			bool flag = Pranks.IsInLevel() && !Plugin.IsMissing(Pranks.GetLocalAvatar());
			if (flag && !_wasInLevel && ShouldSignal())
			{
				Play(_chime);
				Show("Repo Gameplay Tweaks is working  |  Targets: " + Plugin.TargetListText() + "  |  " + Plugin.PatchedCount + " upgrade types hooked", 6f);
			}
			_wasInLevel = flag;
		}
	}

	private static bool ShouldSignal()
	{
		if (!Plugin.ModEnabled.Value || !_enabled.Value)
		{
			return false;
		}
		object localAvatar = Pranks.GetLocalAvatar();
		return !Plugin.IsMissing(localAvatar) && !Plugin.AvatarIsTarget(localAvatar);
	}

	private void Play(AudioClip clip)
	{
		if (_sound.Value && (Object)(object)clip != (Object)null && (Object)(object)_source != (Object)null)
		{
			_source.PlayOneShot(clip, Mathf.Clamp01(_volume.Value));
		}
	}

	private void Show(string message, float seconds)
	{
		Plugin.Log.LogInfo((object)message);
		if (_text.Value)
		{
			_message = message;
			_messageUntil = Time.time + seconds;
		}
	}

	private void OnGUI()
	{
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Expected O, but got Unknown
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Expected O, but got Unknown
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		if (_message != null && !(Time.time > _messageUntil))
		{
			if (_style == null)
			{
				GUIStyle val = new GUIStyle(GUI.skin.label);
				val.fontSize = 20;
				_style = val;
				_shadowStyle = new GUIStyle(_style);
				_shadowStyle.normal.textColor = new Color(0f, 0f, 0f, 0.8f);
				_style.normal.textColor = new Color(0.45f, 1f, 0.6f, 1f);
			}
			GUI.Label(new Rect(22f, 22f, 1200f, 40f), _message, _shadowStyle);
			GUI.Label(new Rect(20f, 20f, 1200f, 40f), _message, _style);
		}
	}

	private static AudioClip MakeChime()
	{
		float[] array = Tone(659.25f, 0.11f);
		float[] array2 = Tone(880f, 0.2f);
		float[] array3 = new float[array.Length + array2.Length];
		Array.Copy(array, array3, array.Length);
		Array.Copy(array2, 0, array3, array.Length, array2.Length);
		return MakeClip("RGT_Chime", array3);
	}

	private static AudioClip MakeBwomp()
	{
		int num = 19844;
		float[] array = new float[num];
		double num2 = 0.0;
		for (int i = 0; i < num; i++)
		{
			float num3 = (float)i / (float)num;
			float num4 = Mathf.Lerp(320f, 110f, num3);
			num2 += Math.PI * 2.0 * (double)num4 / 44100.0;
			float num5 = Mathf.Min(1f, (float)i / 441f) * (1f - num3);
			array[i] = (float)Math.Sin(num2) * 0.5f * num5 + (float)Math.Sin(num2 * 2.0) * 0.15f * num5;
		}
		return MakeClip("RGT_Bwomp", array);
	}

	private static float[] Tone(float freq, float seconds)
	{
		int num = (int)(44100f * seconds);
		float[] array = new float[num];
		for (int i = 0; i < num; i++)
		{
			float num2 = (float)i / (float)num;
			float num3 = Mathf.Min(1f, (float)i / 220.5f) * (1f - num2) * (1f - num2);
			array[i] = (float)Math.Sin(Math.PI * 2.0 * (double)freq * (double)i / 44100.0) * 0.45f * num3;
		}
		return array;
	}

	private static AudioClip MakeClip(string name, float[] data)
	{
		AudioClip val = AudioClip.Create(name, data.Length, 1, 44100, false);
		val.SetData(data, 0);
		return val;
	}
}